Столбец с более высоким количеством элементов первым в индексе при включении диапазона? - PullRequest
0 голосов
/ 08 мая 2018
CREATE TABLE `files` (
  `did` int(10) unsigned NOT NULL DEFAULT '0',
  `filename` varbinary(200) NOT NULL,
  `ext` varbinary(5) DEFAULT NULL,
  `fsize` double DEFAULT NULL,
  `filetime` datetime DEFAULT NULL,
  PRIMARY KEY (`did`,`filename`),
  KEY `fe` (`filetime`,`ext`),          -- This?
  KEY `ef` (`ext`,`filetime`)           -- or This?
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

В таблице миллион строк. Времена файла в основном различны. Существует конечное число ext значений. Таким образом, filetime имеет большую мощность, а ext имеет гораздо меньшую мощность.

Запрос включает в себя ext и filetime:

WHERE ext = '...'
  AND filetime BETWEEN ... AND ...

Какой из этих двух индексов лучше? И почему?

1 Ответ

0 голосов
/ 08 мая 2018

Сначала попробуем FORCE INDEX выбрать либо ef, либо fe. Время слишком короткое, чтобы получить четкое представление о том, что быстрее, но `EXPLAIN показывает разницу:

Сначала форсировать диапазон на filetime. (Примечание: заказ в WHERE не оказывает влияния.)

mysql> EXPLAIN SELECT COUNT(*), AVG(fsize)
    FROM files FORCE INDEX(fe)
    WHERE ext = 'gif' AND filetime >= '2015-01-01'
                      AND filetime <  '2015-01-01' + INTERVAL 1 MONTH;
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+
|  1 | SIMPLE      | files | range | fe            | fe   | 14      | NULL | 16684 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+

Форсирование младшей мощности ext first:

mysql> EXPLAIN SELECT COUNT(*), AVG(fsize)
    FROM files FORCE INDEX(ef)
    WHERE ext = 'gif' AND filetime >= '2015-01-01'
                      AND filetime <  '2015-01-01' + INTERVAL 1 MONTH;
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | files | range | ef            | ef   | 14      | NULL |  538 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+

Очевидно, что rows говорит, что ef лучше. Но давайте проверим трассировку оптимизатора. Вывод довольно громоздкий; Я покажу только интересные части. FORCE не требуется; трассировка покажет оба варианта, а затем выберет лучший.

             ...
             "potential_range_indices": [
                ...
                {
                  "index": "fe",
                  "usable": true,
                  "key_parts": [
                    "filetime",
                    "ext",
                    "did",
                    "filename"
                  ]
                },
                {
                  "index": "ef",
                  "usable": true,
                  "key_parts": [
                    "ext",
                    "filetime",
                    "did",
                    "filename"
                  ]
                }
              ],

...

              "analyzing_range_alternatives": {
                "range_scan_alternatives": [
                  {
                    "index": "fe",
                    "ranges": [
                      "2015-01-01 00:00:00 <= filetime < 2015-02-01 00:00:00"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "rowid_ordered": false,
                    "using_mrr": false,
                    "index_only": false,
                    "rows": 16684,
                    "cost": 20022,               <-- Here's the critical number
                    "chosen": true
                  },
                  {
                    "index": "ef",
                    "ranges": [
                      "gif <= ext <= gif AND 2015-01-01 00:00:00 <= filetime < 2015-02-01 00:00:00"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "rowid_ordered": false,
                    "using_mrr": false,
                    "index_only": false,
                    "rows": 538,
                    "cost": 646.61,               <-- Here's the critical number
                    "chosen": true
                  }
                ],

...

          "attached_conditions_computation": [
            {
              "access_type_changed": {
                "table": "`files`",
                "index": "ef",
                "old_type": "ref",
                "new_type": "range",
                "cause": "uses_more_keyparts"   <-- Also interesting
              }
            }

При fe (сначала столбец диапазона) можно использовать диапазон, но он оценивает сканирование по 16684 строкам для поиска ext='gif'.

С ef (сначала низкая мощность ext) он может использовать оба столбца индекса и более эффективно выполнять детализацию в BTree. Затем он нашел приблизительно 538 строк, все из которых полезны для запроса - дополнительная фильтрация не требуется.

Выводы:

  • INDEX(filetime, ext) используется только первый столбец.
  • INDEX(ext, filetime) использованы оба столбца.
  • Поместить столбцы, участвующие в = тестах сначала в индекс независимо от количества элементов .
  • План запроса не будет выходить за пределы первого столбца 'range'.
  • «Количество элементов» не имеет значения для составных индексов и запросов этого типа .

(«Использование условия индекса» означает, что механизм хранения (InnoDB) будет использовать столбцы индекса помимо того, который использовался для фильтрации. »)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...