Индекс IN и диапазон - PullRequest
       23

Индекс IN и диапазон

0 голосов
/ 09 марта 2011

Мне нужно найти лучший индекс для этого запроса:

SELECT c.id id, type
FROM Content c USE INDEX (type_proc_last_cat)
LEFT JOIN Battles b ON c.id = b.id
WHERE type = 1
    AND processing_status = 1
    AND category IN (13, 19)
    AND status = 4
ORDER BY last_change DESC
LIMIT 100";

Таблицы выглядят так:

mysql> describe Content;
+-------------------+---------------------+------+-----+---------+-------+
| Field             | Type                | Null | Key | Default | Extra |
+-------------------+---------------------+------+-----+---------+-------+
| id                | bigint(20) unsigned | NO   | PRI | NULL    |       |
| type              | tinyint(3) unsigned | NO   | MUL | NULL    |       |
| category          | bigint(20) unsigned | NO   |     | NULL    |       |
| processing_status | tinyint(3) unsigned | NO   |     | NULL    |       |
| last_change       | int(10) unsigned    | NO   |     | NULL    |       |
+-------------------+---------------------+------+-----+---------+-------+

mysql> show indexes from Content;
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name            | Seq_in_index | Column_name       | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
| Content |          0 | PRIMARY             |            1 | id                | A         |        4115 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_last_cat  |            1 | type              | A         |           4 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_last_cat  |            2 | processing_status | A         |          20 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_last_cat  |            3 | last_change       | A         |        4115 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_last_cat  |            4 | category          | A         |        4115 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+


mysql> describe Battles;
+---------------------+---------------------+------+-----+---------+-------+
| Field               | Type                | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------+-------+
| id                  | bigint(20) unsigned | NO   | PRI | NULL    |       |
| status              | tinyint(4) unsigned | NO   |     | NULL    |       |
| status_last_changed | int(11) unsigned    | NO   |     | NULL    |       |
+---------------------+---------------------+------+-----+---------+-------+

mysql> show indexes from Battles;
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Battles |          0 | PRIMARY   |            1 | id          | A         |        1215 |     NULL | NULL   |      | BTREE      |         |
| Battles |          0 | id_status |            1 | id          | A         |        1215 |     NULL | NULL   |      | BTREE      |         |
| Battles |          0 | id_status |            2 | status      | A         |        1215 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

И я получаю вывод, как это:

mysql> explain
    -> SELECT c.id id, type
    -> FROM Content c USE INDEX (type_proc_last_cat)
    -> LEFT JOIN Battles b USE INDEX (id_status) ON c.id = b.id
    -> WHERE type = 1
    ->     AND processing_status = 1
    ->     AND category IN (13, 19)
    ->     AND status = 4
    -> ORDER BY last_change DESC
    -> LIMIT 100;
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+
| id | select_type | table | type   | possible_keys      | key                | key_len | ref                   | rows | Extra                    |
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+
|  1 | SIMPLE      | c     | ref    | type_proc_last_cat | type_proc_last_cat | 2       | const,const           | 1352 | Using where; Using index |
|  1 | SIMPLE      | b     | eq_ref | id_status          | id_status          | 9       | wtm_master.c.id,const |    1 | Using where; Using index |
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+

Проблема в том, что количество строк в таблице содержимого.Похоже, что MySQL не может эффективно использовать как last_change, так и категорию в индексе type_proc_last_cat.Если я переключу порядок last_change и category, будет выбрано меньше строк, но это приведет к сортировке файлов для ORDER BY, что означает, что он извлекает все соответствующие строки из базы данных.Это еще хуже, поскольку в обеих таблицах более 100 000 строк.

Обе таблицы являются InnoDB, поэтому имейте в виду, что ключ PRIMARY добавляется ко всем остальным индексам.Таким образом, индекс, указанный выше как type_proc_last_cat, ведет себя так, как будто он включен (type, processing_status, last_change, category, id).Я знаю, что могу изменить ключ PRIMARY для Battles на (id, status) и удалить индекс id_status (и я могу просто сделать это).

Edit: любое значение для типа, категории, processing_status и statusсоставляет менее 20% от общего значения.last_change и status_last_change являются временными метками Unix.

Редактировать: Если я использую другой индекс с category и last_change в обратном порядке, я получаю это:

mysql> show indexes from Content;
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name            | Seq_in_index | Column_name       | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
| Content |          0 | PRIMARY             |            1 | id                | A         |        4115 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_cat_last  |            1 | type              | A         |           6 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_cat_last  |            2 | processing_status | A         |          26 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_cat_last  |            3 | category          | A         |         228 |     NULL | NULL   |      | BTREE      |         |
| Content |          1 | type_proc_cat_last  |            4 | last_change       | A         |        4115 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+


mysql> explain SELECT c.id id, type FROM Content c USE INDEX (type_proc_cat_last) LEFT JOIN Battles b 
USE INDEX (id_status) ON c.id = b.id WHERE type = 1     AND processing_status = 1     AND category IN (13, 19)     AND status = 4 ORDER BY last_change DESC LIMIT 100;
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+
| id | select_type | table | type  | possible_keys      | key                | key_len | ref                   | rows | Extra                                    |
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+
|  1 | SIMPLE      | c     | range | type_proc_cat_last | type_proc_cat_last | 10      | NULL                  |  165 | Using where; Using index; Using filesort |
|  1 | SIMPLE      | b     | ref   | id_status          | id_status          | 9       | wtm_master.c.id,const |    1 | Using where; Using index                 |
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+

Файловая сортировка беспокоит менякак он говорит мне, MySQL сначала извлекает все соответствующие строки перед сортировкой.Это будет большой проблемой, когда 100 000 +.

1 Ответ

0 голосов
/ 09 марта 2011
Поле

rows в EXPLAIN не отражает количество фактически прочитанных строк. Он отражает количество возможных строк. Также не полагается на LIMIT, потому что LIMIT применяется после того, как план был рассчитан.

Так что тебе не нужно об этом беспокоиться.

Также я бы предложил вам поменять last_change и category на type_proc_last_cat, чтобы mysql мог попытаться использовать последнюю часть индекса (last_change) для целей сортировки.

...