Правильный индекс / запрос при использовании INNER JOIN - PullRequest
1 голос
/ 04 марта 2012

Я не уверен, как создать приличный индекс, который будет правильно фиксировать категорию / log_code.Может быть, мне также нужно изменить свой запрос?Цените любой ввод!

Все SELECTS содержат:

SELECT logentry_id, date, log_codes.log_desc FROM log_entries
INNER JOIN log_codes ON log_entries.log_code = log_codes.log_code 
ORDER BY logentry_id DESC

Запрос может быть таким же, как указано выше, но обычно имеет ГДЕ, чтобы указать категорию лог-кодов для отображения, и / или партнера, и /или клиент.Примеры WHERE:

WHERE partner_id = 1

WHERE log_codes.category_overview = 1

WHERE partner_id = 1 AND log_codes.category_overview = 1

WHERE partner_id = 1 AND customer_id = 1 AND log_codes.category_overview = 1

Структура базы данных:

CREATE TABLE IF NOT EXISTS `log_codes` (
  `log_code` smallint(6) NOT NULL,
  `log_desc` varchar(255),
  `category_mail` tinyint(1) NOT NULL,
  `category_overview` tinyint(1) NOT NULL,
  `category_cron` tinyint(1) NOT NULL,
  `category_documents` tinyint(1) NOT NULL,
  `category_error` tinyint(1) NOT NULL,
  PRIMARY KEY (`log_code`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `log_entries` (
  `logentry_id` int(11) NOT NULL AUTO_INCREMENT,
  `date` datetime NOT NULL,
  `log_code` smallint(6) NOT NULL,
  `partner_id` int(11) NOT NULL,
  `customer_id` int(11) NOT NULL,
  PRIMARY KEY (`logentry_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

РЕДАКТИРОВАТЬ: Добавлены индексы для полей, здесь выводится ПОКАЗАТЬ ИНДЕКСЫ:

+-----------+------------+-----------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table     | Non_unique | Key_name              | Seq_in_index | Column_name           | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+-----------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| log_codes |          0 | PRIMARY               |            1 | log_code              | A         |          97 |     NULL | NULL   |      | BTREE      |         |               |
| log_codes |          1 | category_mail         |            1 | category_mail         | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| log_codes |          1 | category_overview     |            1 | category_overview     | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| log_codes |          1 | category_cron         |            1 | category_cron         | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| log_codes |          1 | category_documents    |            1 | category_documents    | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| log_codes |          1 | category_error        |            1 | category_error        | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
+-----------+------------+-----------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

+-------------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table       | Non_unique | Key_name     | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| log_entries |          0 | PRIMARY      |            1 | logentry_id  | A         |      163020 |     NULL | NULL   |      | BTREE      |         |               |
| log_entries |          1 | log_code     |            1 | log_code     | A         |          90 |     NULL | NULL   |      | BTREE      |         |               |
| log_entries |          1 | partner_id   |            1 | partner_id   | A         |           6 |     NULL | NULL   | YES  | BTREE      |         |               |
| log_entries |          1 | customer_id  |            1 | customer_id  | A         |       20377 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

РЕДАКТИРОВАТЬ 2 : добавлены составные индексы: (log_code, category_overview) и (log_code, category_overview) в log_codes.(customer_id, partner_id) в log_entries.

Вот некоторые выходные данные EXPLAIN (запрос возвращает 66818 строк):

EXPLAIN SELECT log_entries.logentry_id, log_entries.date, log_codes.log_code_desc FROM log_entries
INNER JOIN log_codes ON log_entries.log_code = log_codes.log_code
WHERE log_entries.partner_id = 1 AND log_codes.category_overview = 1 ORDER BY logentry_id DESC
+----+-------------+-------------+--------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+
| id | select_type | table       | type   | possible_keys                       | key        | key_len | ref                  | rows   | Extra                       |
+----+-------------+-------------+--------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+
|  1 | SIMPLE      | log_entries | ref    | log_code,partner_id                 | partner_id | 2       | const                | 156110 | Using where; Using filesort |
|  1 | SIMPLE      | log_codes   | eq_ref | PRIMARY,code_overview,overview_code | PRIMARY    | 2       | log_entries.log_code |      1 | Using where                 |
+----+-------------+-------------+--------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+

Но у меня также есть несколько левов, которые я не думалможет повлиять на дизайн индекса, но они вызывают проблему «Использование временного».Вот вывод EXPLAIN (запрос возвращает 66818 строк):

EXPLAIN SELECT log_entries.logentry_id, log_entries.date, log_codes.log_code_desc FROM log_entries 
INNER JOIN log_codes ON log_entries.log_code = log_codes.log_code 
LEFT JOIN partners ON log_entries.partner_id = partners.partner_id 
LEFT JOIN joined_table1 ON log_entries.t1_id = joined_table1.t1_id 
LEFT JOIN joined_table2 ON log_entries.t2_id = joined_table2.t2_id 
LEFT JOIN joined_table3 ON log_entries.t3_id = joined_table3.t3_id 
LEFT JOIN joined_table4 ON joined_table3.t4_id = joined_table4.t4_id 
LEFT JOIN joined_table5 ON log_entries.t5_id = joined_table5.t5_id 
LEFT JOIN joined_table6 ON log_entries.t6_id = joined_table6.t6_id
WHERE log_entries.partner_id = 1 AND log_codes.category_overview = 1 ORDER BY logentry_id DESC;
+----+-------------+---------------+--------+-------------------------------------+---------------+---------+--------------------------+------+----------------------------------------------+
| id | select_type | table         | type   | possible_keys                       | key           | key_len | ref                      | rows | Extra                                        |
+----+-------------+---------------+--------+-------------------------------------+---------------+---------+--------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | log_codes     | ref    | PRIMARY,code_overview,overview_code | overview_code | 1       | const                    |   54 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | log_entries   | ref    | log_code,partner_id                 | log_code      | 2       | log_codes.log_code       | 1811 | Using where                                  |
|  1 | SIMPLE      | partners      | const  | PRIMARY                             | PRIMARY       | 2       | const                    |    1 | Using index                                  |
|  1 | SIMPLE      | joined_table1 | eq_ref | PRIMARY                             | PRIMARY       | 1       | log_entries.t1_id        |    1 | Using index                                  |
|  1 | SIMPLE      | joined_table2 | eq_ref | PRIMARY                             | PRIMARY       | 1       | log_entries.t2_id        |    1 | Using index                                  |
|  1 | SIMPLE      | joined_table3 | eq_ref | PRIMARY                             | PRIMARY       | 3       | log_entries.t3_id        |    1 |                                              |
|  1 | SIMPLE      | joined_table4 | eq_ref | PRIMARY                             | PRIMARY       | 3       | joined_table3.t4_id      |    1 | Using index                                  |
|  1 | SIMPLE      | joined_table5 | eq_ref | PRIMARY                             | PRIMARY       | 4       | log_entries.t5_id        |    1 | Using index                                  |
|  1 | SIMPLE      | joined_table6 | eq_ref | PRIMARY                             | PRIMARY       | 4       | log_entries.t6_id        |    1 | Using index                                  |
+----+-------------+---------------+--------+-------------------------------------+---------------+---------+--------------------------+------+----------------------------------------------+

Не знаю, хорошая это или плохая идея, но подзапрос, похоже, избавляется от «Использование временного».Вот ОБЪЯСНИТЕ вывод двух общих сценариев.Этот запрос возвращает 66818 строк:

EXPLAIN SELECT log_entries.logentry_id, log_entries.date, log_codes.log_code_desc FROM log_entries INNER JOIN log_codes ON log_entries.log_code = log_codes.log_code
WHERE log_entries.partner_id = 1
AND log_entries.log_code IN (SELECT log_code FROM log_codes WHERE category_overview = 1) ORDER BY logentry_id DESC;
+----+--------------------+-------------+-----------------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+
| id | select_type        | table       | type            | possible_keys                       | key        | key_len | ref                  | rows   | Extra                       |
+----+--------------------+-------------+-----------------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+
|  1 | PRIMARY            | log_entries | ref             | log_code,partner_id                 | partner_id | 2       | const                | 156110 | Using where; Using filesort |
|  1 | PRIMARY            | log_codes   | eq_ref          | PRIMARY,code_overview               | PRIMARY    | 2       | log_entries.log_code |      1 |                             |
|  2 | DEPENDENT SUBQUERY | log_codes   | unique_subquery | PRIMARY,code_overview,overview_code | PRIMARY    | 2       | func                 |      1 | Using where                 |
+----+--------------------+-------------+-----------------+-------------------------------------+------------+---------+----------------------+--------+-----------------------------+

И обзор клиента, запрос возвращает 12 строк:

EXPLAIN SELECT log_entries.logentry_id, log_entries.date, log_codes.log_code_desc FROM log_entries INNER JOIN log_codes ON log_entries.log_code = log_codes.log_code
WHERE log_entries.partner_id = 1 AND log_entries.customer_id = 10000
AND log_entries.log_code IN (SELECT log_code FROM log_codes WHERE category_overview = 1) ORDER BY logentry_id DESC;
+----+--------------------+-------------+-----------------+--------------------------------------------------+--------------+---------+----------------------+------+-----------------------------+
| id | select_type        | table       | type            | possible_keys                                    | key          | key_len | ref                  | rows | Extra                       |
+----+--------------------+-------------+-----------------+--------------------------------------------------+--------------+---------+----------------------+------+-----------------------------+
|  1 | PRIMARY            | log_entries | ref             | log_code,partner_id,customer_id,customer_partner | customer_id  | 4       | const                |   27 | Using where; Using filesort |
|  1 | PRIMARY            | log_codes   | eq_ref          | PRIMARY,code_overview                            | PRIMARY      | 2       | log_entries.log_code |    1 |                             |
|  2 | DEPENDENT SUBQUERY | log_codes   | unique_subquery | PRIMARY,code_overview,overview_code              | PRIMARY      | 2       | func                 |    1 | Using where                 |
+----+--------------------+-------------+-----------------+--------------------------------------------------+--------------+---------+----------------------+------+-----------------------------+

Ответы [ 2 ]

2 голосов
/ 04 марта 2012

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

Все последующие комментариипоэтому должны восприниматься не как абсолютные правила:

Индекс является «хорошим», если он быстро приводит вас к небольшому подмножеству данных, а не если он удаляет только половину данных (например, редко встречается значениев индексе по гендерному столбцу, где в качестве возможных записей указаны только M / F).Итак, насколько уникальны значения в пределах, например, log_code, category_overview и partner_id?

Для данного запроса часто полезно иметь «покрывающий» индекс, который включает в себя все поля, используемые запросом.- однако, если в запросе слишком много полей из одной таблицы, вместо этого вам нужен индекс, включающий поля в выражении «where» или «join», чтобы идентифицировать строку, а затем присоединиться к хранилищу таблицы, чтобы получить всеобязательные поля.

Таким образом, учитывая предоставленную вами информацию, индекс кандидата в log_codes будет включать log_code и category_overview.Аналогично для log_entries для log_code и partner_id.Однако их необходимо оценить с точки зрения их влияния на производительность.

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

1 голос
/ 04 марта 2012

Хорошо, что нашли время, чтобы обновить свой вопрос с запрошенными деталями.Я извиняюсь, если это звучит покровительственно, но удивительно, что количество людей, которые не готовы выделить время, чтобы помочь себе.

Добавление составного индекса через (customer_id, partner_id) в таблицу log_entries должно дать значительноевыгода для последнего из вашего примера, где условия.

Вывод ваших SHOW INDEXES для таблицы log_codes предполагает, что она в настоящее время не заполнена, поскольку показывает NULL для всех, кроме PK.Это тот случай?

РЕДАКТИРОВАТЬ Извините.Просто прочитайте свой комментарий к ответу KAJ, детализирующему содержание таблицы.Возможно, стоит запустить этот оператор SHOW INDEXES снова, так как похоже, что MySQL, возможно, строит свою статистику.

Добавление составного индекса через (log_code, category_overview) для таблицы log_codes должно помочь, но вам нужно проверитьвывод объяснения, чтобы увидеть, используется ли он.

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

ОБНОВЛЕНИЕ Я создал макет вашего набора данных и добавил следующие индексы.Они дают значительное улучшение на основе ваших примеров предложений WHERE -

ALTER TABLE `log_codes`
    ADD INDEX `IX_overview_code` (`category_overview`, `log_code`);

ALTER TABLE `log_entries`
    ADD INDEX `IX_partner_code` (`partner_id`, `log_code`),
    ADD INDEX `IX_customer_partner_code` (`customer_id`, `partner_id`, `log_code`);

Последний индекс довольно дорог с точки зрения дискового пространства и снижения производительности вставки, но дает очень быстрый SELECT на основе вашего последнего примера предложения WHERE.Мой примерный набор данных содержит чуть более 1М записей в таблице log_entries с довольно равномерным распределением по идентификаторам партнера и клиента.Три из ваших примеров предложений WHERE выполняются менее чем за секунду, но один из них, в котором единственным критерием является category_overview, является очень медленным, хотя все еще менее чем за секунду с только 200 тыс. Строк.

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