2016-06-09 111 views
0

我有一張擁有大約1300萬行的表,並且我的主鍵是十六進制值VARBINARY(16)MySQL/MariaDB按分數排序排列

我用的是後續查詢來獲取我的結果:

SELECT * 
FROM dbip 
WHERE ip_start <= INET6_ATON('XXX.XX.XX.XX') 
    AND addr_type = 4 
ORDER BY ip_start DESC 
LIMIT 1; 

但此查詢需要大約0.1秒,它應該是約0.02秒或更少。

我的帶寬的99%是在巴西誰只是131.270行,我在我的數據庫列country列。那麼我可以怎樣在巴西的其餘地方首先在巴西搜索這個IP呢?你認爲這樣我會獲得一些毫秒?

我的表:

CREATE TABLE `dbip` 
(
    `addr_type` TINYINT(1) NOT NULL, 
    `ip_start` VARBINARY(16) NOT NULL, 
    `ip_end` VARBINARY(16) NOT NULL, 
    `country` CHAR(2) NOT NULL, 
    `stateprov` VARCHAR(80) NOT NULL, 
    `city` VARCHAR(80) NOT NULL, 
    `latitude` FLOAT NOT NULL, 
    `longitude` FLOAT NOT NULL, 
    `timezone_offset` FLOAT NOT NULL, 
    `timezone_name` VARCHAR(64) NOT NULL, 
    `isp_name` VARCHAR(128) NOT NULL, 
    `connection_type` VARCHAR(8) NULL DEFAULT NULL, 
    `organization_name` VARCHAR(128) NOT NULL, 
    PRIMARY KEY (`ip_start`) 
) 
COLLATE='utf8_general_ci' 
ENGINE=InnoDB 
; 

這是一個實時數據,所以必須要快。我也接受建議改變我的數據庫,我嘗試了一些與記憶儲存空間,但不接受二進制值索引或不我允許使用<=命令

+0

什麼是您的表格架構? – Alex

+0

你是怎麼確定你的查詢時間應該是「0,02秒或更少」? –

+0

@BobJarvis - 系統最低要求。我需要這樣做纔能有更好的表現 – Guhh

回答

0

這是相當倒模式,需要更多的優化。

首先,您必須將所有認爲可行的所有內容直接引用並分組。索引國家代碼本身將減少選擇時間,即

SELECT * FROM dbip 
    WHERE 
    country="BR" 
    AND addr_type = 4 
    AND ip_start <= INET6_ATON('XXX.XX.XX.') 
    ORDER BY ip_start DESC; 

將縮小select到131270的行。 add_type索引將進一步削減搜索。

其次,規範化那些重複性的價值,否則,你最終會無法保持數據的完整性(想象一些存儲城市名稱的錯字)。另外,將大量數據加載到DBMS中會佔用大量資源。是的,留下加入這些數據是「不方便的」,從長遠來看它會爲您節省。如果沒有標準化,每行將佔用至少530字節,如果結果返回50k行,那麼530bytes x 50k =聚集前容易25MB(按排序順序排列前)

如果沒有LIMIT 1,這個查詢肯定會佔用更多分鐘,因爲它試圖遍歷整個1300萬行,將大量數據聚合到內存。

0

由於幾乎所有的條目都是針對巴西的,所以我們可以忽略country

WHERE addr_type = 4 
    AND ip_start <= INET6_ATON('XXX.XX.XX.') 
    ORDER BY ip_start DESC; 

需要

INDEX(addr_type, ip_start) 

具體地用 「=常數」,則移動到 '範圍' 開始。應該在相同的優化中使用ORDER BY

相反,如果你加上`和國家= 'BR',那麼你需要:

INDEX(country, addr_type, ip_start) 

countryaddr_type可以以任何順序,但ip_start必須是最後一個。)

由於LENGTH(INET6_ATON(...))的值爲4或16,因此可以刪除類型,具體取決於它們是IPv4還是IPv6。

我不認爲你原來的查詢不應該花那麼長時間。請進行進一步調試:

FLUSH STATUS; 
SELECT ...; 
SHOW SESSION STATUS LIKE 'Handler%';