4,000万のエントリを持つデータベースがあり、次のWHERE
句を使用してクエリを実行したい
...
WHERE
`POP1` IS NOT NULL
&& `VT`='ABC'
&& (`SOURCE`='HOME')
&& (`alt` RLIKE '^[AaCcGgTt]$')
&& (`ref` RLIKE '^[AaCcGgTt]$')
&& (`AA` RLIKE '^[AaCcGgTt]$')
&& (`ref` = `AA` || `alt` = `AA`)
LIMIT 10 ;
POP1
は、NULLの場合もある浮動列です。 POP1 IS NOT NULL
は、エントリの約50%を除外する必要があります。そのため、最初にそれを配置しました。他のすべての用語は、数をごくわずかに減らします。
とりわけ、私はインデックスを設計しましたpop1_vt_source
、これは使用されていないようですが、最初の列がvt
のインデックスが使用されています。 EXPLAIN-出力:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | myTab | ref | vt_source_pop1_pop2,pop1_vt_source,... | vt_source_pop1_pop2 | 206 | const,const | 20040021 | Using where |
なぜインデックスはpop1
最初の列は使用されていませんか?一般にNOT
またはNULL
が原因です。インデックスとWHERE句のデザインを改善するにはどうすればよいですか?テーブルの最初の100エントリには10個の一致が含まれているはずですが、10エントリに制限する場合でも、クエリには30秒以上かかります。
それはNOT NULL
です:
CREATE TEMPORARY TABLE `myTab` (`notnul` FLOAT, `nul` FLOAT);
INSERT INTO `myTab` VALUES (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2);
SELECT * FROM `myTab`;
与える:
+--------+------+
| notnul | nul |
+--------+------+
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
+--------+------+
インデックスを作成します。
CREATE INDEX `notnul_nul` ON `myTab` (`notnul`, `nul`);
CREATE INDEX `nul_notnul` ON `myTab` (`nul`, `notnul`);
SHOW INDEX FROM `myTab`;
与える:
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| myTab | 1 | notnul_nul | 1 | notnul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | notnul_nul | 2 | nul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | nul_notnul | 1 | nul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | nul_notnul | 2 | notnul | A | 12 | NULL | NULL | YES | BTREE | | |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
選択について説明します。あなたがNOT NULL
を使用していても、MySQLはインデックスを使用しているようです:
EXPLAIN SELECT * FROM `myTab` WHERE `notnul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | index | notnul_nul | notnul_nul | 10 | NULL | 12 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | range | nul_notnul | nul_notnul | 5 | NULL | 6 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
ただし、NOT NULL
とNULL
を比較すると、MySQLはNOT NULL
を使用するときに他のインデックスを優先するようです。これは明らかに情報を追加しませんが。これは、MySQLがtype-columnで確認できるようにNOT NULL
を範囲として解釈するためです。回避策があるかどうかはわかりません:
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NULL && notnul=2;
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| 1 | SIMPLE | myTab | ref | notnul_nul,nul_notnul | notnul_nul | 10 | const,const | 1 | Using where; Using index |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL && notnul=2;
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | range | notnul_nul,nul_notnul | notnul_nul | 10 | NULL | 1 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
NULL
は特別な値なので、MySQLでの実装が改善されると思います。おそらくほとんどの人はNOT NULL
の値に興味を持っています。
問題はNULL値ではありません。インデックスの選択性です。あなたの例では、source, pop1
の選択性はpop1
だけの選択性よりも優れています。 where
句の条件の多くをカバーしているため、ページヒットを減らす可能性が高くなります。
行数を50%減らすだけで十分だと思うかもしれませんが、実際はそうではありません。 where
句のインデックスの利点は、読み込まれるページ数を減らすことです。ページに平均して、NULL以外の値を持つレコードが少なくとも1つある場合、インデックスを使用するメリットはありません。また、ページごとに10個のレコードがある場合、ほとんどすべてのページにそれらのレコードの1つが含まれます。
(pop1, vt, source)
でインデックスを試してみてください。オプティマイザはそれを選択する必要があります。
しかし結局のところ、where
句がレコードを失っている場合-規則はありませんが、20%としましょう-おそらくインデックスは役に立ちません。 1つの例外は、インデックスにallクエリに必要な列が含まれる場合です。次に、各レコードのデータページを読み込まなくてもクエリを実行できます。
また、インデックスが使用され、選択性が高い場合、インデックスを使用した場合のパフォーマンスは、インデックスを使用しない場合よりも低下する可能性があります。