Почему SELECT DISTINCT в индексированном столбце не мгновенный? - PullRequest
0 голосов
/ 17 мая 2019

У меня есть такая таблица, в которой хранятся конфигурации различных запущенных программ.Это выглядит примерно так:

+--------------+---------------+------+-----+---------+-------+
| Field        | Type          | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| Date         | date          | YES  | MUL | NULL    |       |
| Program      | varchar(20)   | YES  | MUL | NULL    |       |
| ConfigFile   | int(11)       | YES  |     | NULL    |       |
| Parameter    | varchar(20)   | YES  |     | NULL    |       |
| Value        | varchar(20)   | YES  |     | NULL    |       |
+--------------+---------------+------+-----+---------+-------+

Поле ConfigFile содержит номер файла конфигурации - для некоторых программ можно выбрать более одного файла конфигурации.

У него есть пара индексов, вот так:

+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lists |          1 | Date     |            1 | Date         | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Date     |            2 | Program      | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Date     |            3 | Parameter    | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Program  |            1 | Program      | A         |        4676 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Program  |            2 | Parameter    | A         |      183706 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

Теперь давайте скажем, что я хочу знать, каковы параметры для данной программы.Похоже, я должен быть в состоянии сделать что-то вроде этого:

SELECT DISTINCT Parameter FROM params WHERE Program = 'MyProgram';

Это имеет следующий план объяснения:

+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys  | key     | key_len | ref   | rows      | filtered | Extra                    |
+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+
|  1 | SIMPLE      | params | NULL       | ref  | Date,Program   | Program | 23      | const | 137203382 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+

Есть что-то вроде 15 различных вариантов для Program,и, возможно, от 10 до 100 значений Parameter для каждой программы.

С моим пониманием того, как работает индекс базы данных, я ожидаю, что это завершится мгновенно.В частности, я ожидаю, что базовой структурой данных будет дерево бинарного поиска с 15 узлами, которое я ищу, чтобы найти то, которое соответствует моей программе;после нахождения моей программы, я перехожу ко второму бинарному поисковому дереву с, возможно, 100 или менее узлами, которое я тогда просто обойду.

Когда я на самом деле запускаю запрос, хотя онв итоге занимает несколько минут.

Для меня это говорит о том, что возможно существует несколько копий одного и того же значения в дереве двоичного поиска, по одной на узел таблицы.Это то, что происходит, и, если да, что я могу сделать, чтобы смягчить эту ситуацию?

Я подумал о том, чтобы иметь одну таблицу с уникальными тройками (Дата, Программа, Параметр) и иметь отношение, но я неуверен, как выполнить массовую вставку данных в этой ситуации.И если я ошибаюсь из-за того, почему это так медленно, то, конечно, это даже не поможет.

1 Ответ

1 голос
/ 26 мая 2019

Вторичные индексы InnoDB B + Tree не формируются таким образом.Подумайте об этом так:

  1. Для каждой строки создайте строку, состоящую из Program, Parameter, PK.
  2. Сортировка этих строк.
  3. Разложите их на BTree.

Примечание: не было намека на разделение на Program.Что если 99,9% программ были в программе № 5?Это было бы довольно несбалансированным BTree.Удобно для вашего одного редкого запроса, но медленнее для большинства других запросов.

При хорошо сбалансированном дереве B + ваш запрос должен:

  1. Развернуть BTree, чтобы найтипервый «ряд» для Program = 'MyProgram'
  2. Пройдите вперед через листовые узлы дерева B +, используя «+» для перехода от одного листового блока к следующему.
  3. Во время ходьбы,отслеживать каждый новый Parameter.
  4. Выход при сбое Program = 'MyProgram'.

Примечания:

  • DISTINCT было легко реализовано в моемшаг 3, понимая, как упорядочены элементы.
  • «Использование индекса» говорит, что индекс «покрывал» - поскольку вам нужны были только Program и Parameter (а это были столбцы в INDEX).PK также неявно доступен для «покрытия».
  • 15, которые вы предоставили, не согласны с кардинальностью «4676».Но это только указывает на то, что статистика иногда довольно далека.(Статистика не влияет на оптимизацию этого запроса.)

Я рассмотрел вопрос об использовании одной таблицы с уникальными тройками (Дата, Программа, Параметр)

Да,Наличие такой таблицы сделает ваш запрос намного быстрее.Но стоит ли поддерживать такое?

Еще одна вещь, которую таблица позволит вам сделать, - это нормализовать эти 3 столбца в один MEDIUMINT UNSIGNED (только 3 байта) вместо, возможно, 30 байтов, используемых в настоящее время.средний ряд.Опять же, будет ли сложность JOINs и т. Д. Перевесить пользу?Это сократило бы объем диска, возможно, на 50%.

...