Як розробити індекси для стовпців із значеннями NULL в MySQL?


11

У мене база даних з 40 мільйонами записів і хочу запускати запити з наступним 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першим стовпцем. ПОЯСНЕННЯ-вихід:

| 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загалом. Як я можу вдосконалити конструкцію своїх індексів та пунктів, де БЕЗ? Навіть при обмеженні на 10 запитів запит займає більше 30 секунд, хоча перші 100 записів у таблиці повинні містити 10 збігів.

Відповіді:


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 NULLі NULL, здається, що MySQL віддає перевагу іншим індексам при використанні NOT NULL. Хоча це, очевидно, не додає жодної інформації. Це тому, що MySQL інтерпретує 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 |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+

Я думаю, що в MySQL може бути краща реалізація, оскільки NULLце особливе значення. Напевно, більшість людей цікавляться NOT NULLцінностями.


3

Проблема не в значеннях NULL. Це вибірковість індексу. У вашому прикладі вибірковість source, pop1кращої, ніж вибірковість справедливих pop1. Він охоплює більше умов цього whereпункту, тому швидше зменшується кількість звернень до сторінки.

Ви можете подумати, що зменшити кількість рядків на 50% досить, але насправді це не так. Користь індексів у whereпункті полягає у зменшенні кількості прочитаних сторінок. Якщо сторінка в середньому має принаймні один запис із значенням, що не має значення NULL, використання індексу не виграє. І якщо на кожній сторінці є 10 записів, то майже на кожній сторінці буде один із цих записів.

Ви можете спробувати індекс на (pop1, vt, source). Оптимізатор повинен вибрати це.

Врешті-решт, якщо whereстаття втрачає записи - немає правила, але скажімо, 20% - то індекс, ймовірно, не допоможе. Винятком буде, коли індекс містить усі стовпці, необхідні для запиту. Тоді він може задовольнити запит, не вносячи на сторінку даних для кожного запису.

І якщо індекс звикне і вибірковість висока, то продуктивність з індексом може бути гіршою, ніж продуктивність без нього.


Я думаю, що це дійсно саме діапазони, що викликають різницю (див. Мою відповідь). Хоча я думаю, що це може бути краще реалізовано в MySQL, оскільки більшість людей цікавляться NOT NULLколонками.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.