2016-07-14 60 views
1

我有第一個表包含我的ips存儲爲整數(500K行),第二個包含黑色列表ips的範圍和黑名單的原因(10M行) 這裏是表結構:MARIADB:索引沒有用於一個範圍加入選擇

CREATE TABLE `black_lists` (
    `id` INT(11) NOT NULL AUTO_INCREMENT, 
    `ip_start` INT(11) UNSIGNED NOT NULL, 
    `ip_end` INT(11) UNSIGNED NULL DEFAULT NULL, 
    `reason` VARCHAR(3) NOT NULL, 
    `excluded` TINYINT(1) NULL DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    INDEX `ip_range` (`ip_end`, `ip_start`), 
    INDEX `ip_start` (`ip_start`), 
    INDEX `ip_end` (`ip_end`), 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB 
AUTO_INCREMENT=10747741 
; 

CREATE TABLE `ips` (
    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Id ips', 
    `idhost` INT(11) NOT NULL COMMENT 'Id Host', 
    `ip` VARCHAR(45) NULL DEFAULT NULL COMMENT 'Ip', 
    `ipint` INT(11) UNSIGNED NULL DEFAULT NULL COMMENT 'Int ip', 
    `type` VARCHAR(45) NULL DEFAULT NULL COMMENT 'Type', 
    PRIMARY KEY (`id`), 
    INDEX `host` (`idhost`), 
    INDEX `index3` (`ip`), 
    INDEX `index4` (`idhost`, `ip`), 
    INDEX `ipsin` (`ipint`) 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB 
AUTO_INCREMENT=675651; 

我的問題是,當我嘗試運行此查詢不使用索引,它需要一個永恆的完成:

select i.ip,s1.reason 
from ips i 
    left join black_lists s1 on i.ipint BETWEEN s1.ip_start and s1.ip_end; 

我使用MariaDB 10.0.16

+1

如果您編寫了一個至少有* some *實際信息的查詢,那麼在哪裏開始挖掘數據,那麼將使用索引。爲什麼不花20秒*思考你在問這樣一個問題之前做了什麼?另外,你知道什麼是索引嗎?這不是什麼黑魔法的事情,讓查詢變得非常快速。我建議你在查詢不能使用索引之前尋找索引是什麼以及它們是如何工作的,即使你有一個仙子授予的願望 - 她不能使這個查詢使用索引。 –

+0

我問這個問題,因爲這個查詢使用索引:「選擇s1.reason 從 black_lists s1在111111111 BETWEEN s1.ip_start和s1。ip_end;「,所以我假設我做了一個左連接,就像這個循環一樣 –

+0

但是你在這裏提供了一個**值**,所以MySQL知道從哪裏開始尋找,如果你沒有提供實際值(數字如111111111),那麼除了查看所有內容並吐出大量記錄外,它可以做些什麼? –

回答

2

確實。

優化器不知道start..end值是不重疊的,也沒有關於它們的其他明顯信息。因此,它可以做的最好的就是決定是那些

之間
s1.ip_start <= i.ipint -- and use INDEX(ip_start), or 
s1.ip_end >= i.ipint -- and use INDEX(ip_end) 

可能導致被掃描的一半表向上。

在2個步驟中,您可以實現所需的目標一個 ip;讓我們說@ip:

SELECT ip_start, reason 
    FROM black_lists 
    WHERE ip_start <= @ip 
    ORDER BY ip_start DESC 
    LIMIT 1 

但在那之後,你需要看看是否對應於ip_start的ip_end是決定你是否有一個列入黑名單的項目之前< = @ip。

SELECT reason 
    FROM (...) a -- fill in the above query 
    JOIN black_lists b USING(ip_start) 
    WHERE b.ip_end <= @ip 

這將返回reason或無行。

儘管複雜,它會非常快。但是,你似乎有一組IP來檢查。這使得它更加複雜。對於black_lists,似乎不需要id。建議你只用2代替4個指標:

PRIMARY KEY(ip_start, ip_end), 
INDEX(ip_end) 

ips,是不是ip獨特之處?如果是這樣,如果獲得擺脫id和變更5項指標,以3:

PRIMARY KEY(idint), 
INDEX(host, ip), 
INDEX(ip) 

您已同意綽綽有餘在VARCHAR對於IPv6,但不是在INT UNSIGNED

More discussion