оптимизация SQL-запроса с использованием внутреннего соединения и порядка - PullRequest
1 голос
/ 11 ноября 2009

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

EXPLAIN SELECT SQL_NO_CACHE `groups`.*
FROM `groups`
INNER JOIN `memberships` ON `groups`.id = `memberships`.group_id
WHERE ((`memberships`.user_id = 1) 
  AND (`memberships`.`status_code` = 1 AND `memberships`.`manager` = 0))
ORDER BY groups.created_at DESC LIMIT 5;`

+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table       | type   | possible_keys            | key     | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | memberships | ref    | grp_usr,grp,usr,grp_mngr | usr     | 5       | const                                       |    5 | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | groups      | eq_ref | PRIMARY                  | PRIMARY | 4       | sportspool_development.memberships.group_id |    1 |                                              | 
+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
2 rows in set (0.00 sec)


    +--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name                          | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+
| groups |          0 | PRIMARY                           |            1 | id              | A         |           6 |     NULL | NULL   |      | BTREE      |         | 
| groups |          1 | index_groups_on_name              |            1 | name            | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_privacy_setting   |            1 | privacy_setting | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_created_at        |            1 | created_at      | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_id_and_created_at |            1 | id              | A         |           6 |     NULL | NULL   |      | BTREE      |         | 
| groups |          1 | index_groups_on_id_and_created_at |            2 | created_at      | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
+--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+


     +-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table       | Non_unique | Key_name                                                 | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| memberships |          0 | PRIMARY                                                  |            1 | id          | A         |           2 |     NULL | NULL   |      | BTREE      |         | 
| memberships |          0 | grp_usr                                                  |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          0 | grp_usr                                                  |            2 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp                                                      |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | usr                                                      |            1 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp_mngr                                                 |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp_mngr                                                 |            2 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            2 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            3 | status_code | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            4 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            1 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            2 | status_code | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            3 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
+-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

Ответы [ 5 ]

0 голосов
/ 12 ноября 2009

Спасибо за публикацию сведений об используемых вами индексах.

Я проверял это и пытался опустить некоторые индексы. Максимальный индекс memberships.complex_index, который служит индексом покрытия . Это позволяет запросу достигать своих результатов, читая только индекс; ему вообще не нужно читать строки данных для memberships.

Ни один из индексов groups не имеет значения. Кажется, использовать сортировку файлов, несмотря ни на что. Файловая сортировка в MySQL просто означает, что она выполняет сканирование таблицы, которое в некоторых случаях может быть менее затратным , чем использование индекса. Например, если каждая строка groups должна быть прочитана для получения результата запроса, зачем вообще делать ненужный двойной поиск с использованием индекса? Оптимизатор может распознавать эти случаи и может надлежащим образом отказаться от использования индекса.

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

0 голосов
/ 11 ноября 2009

A (user_id, status_code, manager) (в любом порядке) поможет индекс memberships.

Отказ от сортировки будет затруднен, потому что тогда вам нужно начать объединение в таблице групп, что означает, что вы не можете использовать все (предположительно довольно избирательные) предложения where, которые ссылаются на таблицу memberships, пока она не станет слишком большой. поздно.

0 голосов
/ 11 ноября 2009

Есть ли у вас все "очевидные" одноколонные индексы для полей, к которым вы присоединяетесь, полей в вашем предложении where и в поле selected_at, по которому вы упорядочиваете?

0 голосов
/ 11 ноября 2009

Беда в том, что вам нужен индекс по группам, чтобы исключить сортировку файлов, но все ваши условия для членства.

Попробуйте добавить индекс по группам (id, creation_at).

Если это не сработает, попробуйте обмануть оптимизатор следующим образом, используя подзапрос (сохраняя вышеупомянутый индекс по группам):

SELECT SQL_NO_CACHE `groups`.*
    FROM `groups`
    INNER JOIN (select group_id from `memberships`
       WHERE 
           `memberships`.user_id = 1
           AND `memberships`.`status_code` = 1
           AND `memberships`.`manager` = 0
    ) m on m.group_id=`groups`.id
    ORDER BY groups.created_at DESC LIMIT 5;

Должен быть индекс как минимум membershipships.user_id, но вы также можете получить некоторую выгоду от такого индекса, как (user_id, status, manager). Я предполагаю, что status и manager - это флаги, которые не имеют большого диапазона возможных значений, так что это не так важно, пока есть индекс user_id.

0 голосов
/ 11 ноября 2009
  • Индекс для memberships столбец user_id (должен уже быть, если это PK)
  • Индекс по memberships столбцам status_code и manager (оба по одному индексу)
  • Индекс по groups столбец created_at (по умолчанию DESC, если возможно, я не знаю, можно ли в mySql)

Это то, что я делал бы в MS SQL Server, но я думаю, что такую ​​же оптимизацию можно использовать и в mySql.

...