2016-07-14 4 views
1

整数(500k行)として保存された自分のipsを含む最初のテーブルと、ブラックリストされた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

このクエリではインデックスを使用していたため、この質問が表示されました。 "select s1.reason black_lists s1 on 111111111 s1.ip_startとs1。ip_end; "なので、もし私が左の結合をしたとすれば、これはループのようになります。 –

+0

しかし、ここで** value **を指定して、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;

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

しかし、その後、あなたはそのip_startに対応するip_endあなたはブラックリストされたアイテムを持っているかどうかを決定する前に< = @ipであるかどうかを確認する必要がありますのは、@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つのインデックスを置き換える提案:ips

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

ipが一意ではないでしょうか?もしそうなら、idあれば取り除くと3に5つのインデックスを変更します。

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

あなたはなくINT UNSIGNEDで、IPv6のVARCHARに十分以上を許可しています。

More discussion

関連する問題