MySQLでNULL値を持つ列のインデックスを設計する方法は?


11

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 ;

POP1NULLにすることもできる浮動列です。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秒以上かかります。

回答:


10

それは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      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

選択について説明します。MySQLはインデックスを使用しているようですNOT NULL

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 NULLNULL、MySQLはを使用するときに他のインデックスを優先するようですNOT NULL。これは明らかに情報を追加しませんが。これは、MySQL NOT NULLがtype-columnで確認できる範囲として解釈するためです。回避策があるかどうかはわかりません:

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価値観に興味を持っています。


3

問題はNULL値ではありません。インデックスの選択性です。あなたの例では、の選択性はの選択性source, pop1よりも優れていpop1ます。where条項の条件の多くをカバーしているので、ページヒットを減らす可能性が高くなります。

行数を50%減らすだけで十分だと思うかもしれませんが、実際はそうではありません。where句のインデックスの利点は、読み込まれるページの数を減らすことです。ページに平均して、NULL以外の値を持つレコードが少なくとも1つある場合、インデックスを使用するメリットはありません。また、ページあたりのレコード数が10の場合、ほとんどすべてのページにそれらのレコードの1つが含まれます。

あなたは上のインデックスを試すかもしれません(pop1, vt, source)。オプティマイザはそれを選択する必要があります。

しかし結局のところ、where条項がレコードを失い続けている場合(ルールはありませんが、20%としましょう)、インデックスはおそらく役に立ちません。1つの例外は、クエリに必要なすべての列がインデックスに含まれている場合です。次に、各レコードのデータページを読み込まずにクエリを実行できます。

また、インデックスが使用され、選択性が高い場合、インデックスを使用した場合のパフォーマンスは、インデックスを使用しない場合よりも低下する可能性があります。


私は本当に違いを引き起こす範囲だと思います(私の答えを見てください)。ほとんどの人がNOT NULLカラムに興味を持っているので、MySQLでよりよく実装できると思いますが。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.