Выберите запрос с тремя, где условия медленные, но тот же запрос с любой комбинацией двух из трех, где условия быстрые - PullRequest
0 голосов
/ 31 декабря 2018

У меня есть следующий запрос:

SELECT table_1.id

FROM
table_1
LEFT JOIN table_2 ON (table_1.id = table_2.id)

WHERE
table_1.col_condition_1 = 0
AND table_1.col_condition_2 NOT IN (3, 4)
AND (table_2.id is NULL OR table_1.date_col > table_2.date_col)

LIMIT 5000;

И у меня есть следующие ключи и индексы:

  • первичный ключ table_1.id.
  • индекс включенtable_1.col_condition_1
  • индекс для table_1.col_condition_2
  • составной индекс для table_1.col_condition_1 и table_1.col_condition_2

Подбираются правильные индексы.Объяснение запроса:

+--+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------------+---------+------------+----------+-----------------------+--+
|  | id | select_type |  table  |  type  |                            possible_keys                            |          key          | key_len |    ref     |   rows   |         Extra         |  |
+--+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------------+---------+------------+----------+-----------------------+--+
|  |  1 | SIMPLE      | table_1 | range  | "the composite index", col_condition_1 index ,col_condition_2 index | "the composite index" |       7 |            | 11819433 | Using index condition |  |
|  |  1 | SIMPLE      | table_2 | eq_ref | PRIMARY,id_UNIQUE                                                   | PRIMARY               |       8 | table_1.id |        1 | Using where           |  |
+--+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------------+---------+------------+----------+-----------------------+--+

таблица_1 содержит ~ 60 мм записей, а таблица_2 содержит ~ 4 мм записей.

Запрос возвращает результат в течение 60 секунд.

Интересно то, что:

SELECT table_1.id

FROM
table_1
LEFT JOIN table_2 ON (table_1.id = table_2.id)

WHERE
table_1.col_condition_1 = 0
AND table_1.col_condition_2 NOT IN (3, 4)

LIMIT 5000;

требует 145 мс для возврата результата и имеет те же индексы, что и первый запрос.

SELECT table_1.id

FROM
table_1
LEFT JOIN table_2 ON (table_1.id = table_2.id)

WHERE
table_1.col_condition_1 = 0
AND (table_2.id is NULL OR table_1.date_col > table_2.date_col)

LIMIT 5000;

занимает 174 мс длявернуть результат.

Объяснение запроса:

+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-------------+
| id | select_type |  table  |  type  |                            possible_keys                            |       key       | key_len |    ref     |   rows   |    Extra    |
+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-------------+
|  1 | SIMPLE      | table_1 | ref    | "the composite index", col_condition_1 index ,col_condition_2 index | col_condition_1 |       2 | const      | 30381842 | NULL        |
|  1 | SIMPLE      | table_2 | eq_ref | PRIMARY,id_UNIQUE                                                   | PRIMARY         |       8 | table_1.id |        1 | Using where |
+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-------------+

И

SELECT table_1.id

FROM
table_1
LEFT JOIN table_2 ON (table_1.id = table_2.id)

WHERE
table_1.col_condition_2 NOT IN (3, 4)
AND (table_2.id is NULL OR table_1.date_col > table_2.date_col)

LIMIT 5000;

для возврата результата требуется около 1 секунды.

Объяснение запроса:

+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-----------------------+
| id | select_type |  table  |  type  |                            possible_keys                            |       key       | key_len |    ref     |   rows   |         Extra         |
+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-----------------------+
|  1 | SIMPLE      | table_1 | range  | "the composite index", col_condition_1 index ,col_condition_2 index | col_condition_2 |       5 |            | 36254294 | Using index condition |
|  1 | SIMPLE      | table_2 | eq_ref | PRIMARY,id_UNIQUE                                                   | PRIMARY         |       8 | table_1.id |        1 | Using where           |
+----+-------------+---------+--------+---------------------------------------------------------------------+-----------------+---------+------------+----------+-----------------------+

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

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

Также, есть ли способ оптимизировать этот запрос?

Tспасибо.

РЕДАКТИРОВАТЬ:

создать таблицы:

table_1:

CREATE TABLE `table_1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `col_condition_1` tinyint(1) DEFAULT '0',
  `col_condition_2` int(11) DEFAULT NULL,
  `date_col` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `compositeidx` (`col_condition_1`,`col_condition_2`),
  KEY `col_condition_1_idx` (`col_condition_1`),
  KEY `col_condition_2_idx` (`col_condition_2`)
) ENGINE=InnoDB AUTO_INCREMENT=68272192 DEFAULT CHARSET=utf8

table_2:

CREATE TABLE `table_2` (
  `id` bigint(20) NOT NULL,
  `date_col` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Ответы [ 3 ]

0 голосов
/ 01 января 2019
  • OR является убийцей производительности.
  • Иногда использование UNION вместо OR может ускорить запрос.
  • Возможно, в одном случае 5000 были "в начале "комбинированных таблиц, но не в другом случае.
  • Использование LIMIT без ORDER BY сомнительно.
  • Поскольку PK является уникальным ключом, он является избыточнымтакже объявлять id_UNIQUE.
  • INDEX(a) не нужно, если у вас также есть INDEX(a,b).
  • Если имеется только 4 значения, IN (1, 2) может бытьбыстрее, чем NOT IN (3, 4).
  • Необычно, когда две таблицы используют один и тот же ПК.Почему у вас есть отношение 1: 1?
  • Мы могли бы получить дополнительную информацию, если бы увидели настоящие имена столбцов.
0 голосов
/ 01 января 2019

Подобные проблемы, как правило, требуют тестирования и проверки того, насколько хорошо они работают.

Таким образом, начните с этого:

SELECT
table_1.id
FROM
table_1
LEFT JOIN table_2
ON table_1.id = table_2.id
AND table_1.date_col <= table_2.date_col
WHERE
table_1.col_condition_1 = 0
AND table_1.col_condition_2 NOT IN (3, 4)
AND table_2.id is NULL

LIMIT 5000;

Логические рассуждения о том, почему это эквивалентноВаш запрос: оператор WHERE исходного запроса (table_2.id is NULL OR table_1.date_col > table_2.date_col) может быть суммирован как «Включать только записи таблицы_1, которые НЕ имеют записи таблицы_2, или где запись таблицы_2 раньше (или равна) записи таблицы_1.

Моя версия запроса использует анти-объединение для исключения всех записей таблицы_1, где они существуют, таблицы_2, которая предшествует (или равна) записи таблицы_1.

Индексы

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

Для таблицы_2: (id,date_col)

Для таблицы_1: (col_condition_1,id,date_col,col_condition_2)

Пожалуйста, попробуйте мой запрос и индексы, и сообщите результаты (включая план EXPLAIN).

0 голосов
/ 31 декабря 2018

Попробуйте разделить существующий SQL на две части и посмотреть, каково время выполнения для каждой.Надеюсь, это даст вам информацию о том, какая часть отвечает за медлительность:

часть 1:

SELECT table_1.id
  FROM table_1
  LEFT JOIN table_2
    ON (table_1.id = table_2.id)
 WHERE table_1.col_condition_1 = 0
   AND table_1.col_condition_2 NOT IN (3, 4)
   AND table_2.id is NULL

и часть 2 (обратите внимание на внутреннее соединение здесь):

SELECT table_1.id
  FROM table_1
  JOIN table_2
    ON (table_1.id = table_2.id)
 WHERE table_1.col_condition_1 = 0
   AND table_1.col_condition_2 NOT IN (3, 4)
   AND table_1.date_col > table_2.date_col

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

Я не думаю, что составной индекс вообще поможет при вашем выборе.

Это говорит о том, что труднодиагностируйте, почему три условия вместе могут негативно повлиять на производительность.Похоже, это связано с вашим распределением данных.Не уверен насчет mySql, но в Oracle статистические сборы по этим таблицам будут иметь значение.

Надеюсь, это поможет.

...