MySql многораздельная таблица - выбор между диапазоном дат, используя PK, очень медленный по сравнению с предложением даты IN (...) - PullRequest
0 голосов
/ 01 апреля 2019

У меня очень большая таблица, состоящая примерно из 3 миллионов записей в день.

Следующий запрос такой медленный

EXPLAIN SELECT *
FROM summary_by_to_days_range 
WHERE(record_date BETWEEN '2019-03-12' AND '2019-03-15')
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;

Со следующими результатами:

+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
|  rows   | filtered |    Extra    | possible_keys |   key   |                     partitions                      |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
| 9072566 |        4 | Using where | PRIMARY       | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+

По сравнению с

EXPLAIN SELECT *
FROM summary_by_to_days_range 
WHERE(record_date IN ('2019-03-12','2019-03-13','2019-03-14','2019-03-15'))
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;

С гораздо лучшими результатами:

+------+----------+-------------+---------------+---------+-----------------------------------------------------+
| rows | filtered |    Extra    | possible_keys |   key   |                     partitions                      |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
|   16 |      100 | Using where | PRIMARY       | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+

И я не могу понять, почему .. Я предоставляю значения PK, единственное отличие - это предложение Between date!

Схема таблицы

  `CREATE TABLE summary_by_to_days_range (
  `record_date` date NOT NULL,
  `unit_id` int(11) NOT NULL,
  `enum_key` int(11) NOT NULL,
  `str_value` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE (TO_DAYS(record_date))
(PARTITION START_h VALUES LESS THAN (0) ENGINE = InnoDB,
 PARTITION from20181231 VALUES LESS THAN (737425) ENGINE = InnoDB,
 PARTITION from20190101 VALUES LESS THAN (737426) ENGINE = InnoDB,
.
.
PARTITION future VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`

Я также пытался выполнить разбиение по ключу, по столбцу диапазона по хэшу функции DAYOFYEAR (), что привело к тому же разочаровывающему результату.

Любой

1 Ответ

1 голос
/ 21 апреля 2019

Обычно неэффективно начинать PRIMARY KEY со столбцов столбца «ключа раздела».В конце концов, сначала идет «обрезка разделов»;зачем тогда фильтровать по тому же материалу?

Обычно неэффективно запускать любой составной индекс со столбцом, который будет использоваться в тесте "диапазона".Это тонко, но я думаю, что это объясняет разницу, которую вы видите.С IN (ваш второй запрос) можно использовать больше полей в PK, тем самым он работает быстрее.

И, нет, оптимизатор достаточно умен, чтобы понять, как работают «даты».В противном случае он может выполнить ваш второй запрос так же быстро, как и первый.(Это грубо касается некоторых комментариев.)

(для справки)

WHERE record_date BETWEEN '2019-03-12' AND '2019-03-15'
  AND unit_id = 1148210
  AND enum_key IN (9, 10, 38, 311)

`record_date` date NOT NULL,
PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)

PARTITION BY RANGE (TO_DAYS(record_date))

Давайте рассмотрим оба моих комментария следующим образом.

Измените на

PRIMARY KEY(unit_id, enum_key, record_date)

С этим ПК либо вашего SELECTs будет

  1. Сокращаться до 4 разделов (как раньше)
  2. Быстро фильтровать вниздо желаемого unit_id.(Я подозреваю, что именно в этом и заключается большая часть эффективности.)
  3. Прыгайте по строкам для enum_key
  4. Проверьте правильность record_date.

Я рад установить разделы start и future.(Возможно, вы уже прочитали this .)

Примечание: может быть несколько неэффективно иметь более 50 разделов.Если у вас есть (или будет) больше, рассмотрите возможность использования еженедельных или ежемесячных разделов.Это будет иметь небольшое влияние на мой ПК, но только до шага 4.

Что касается PARTITION BY HASH ... Я обнаружил нет прирост производительности при его использовании,(Или, по крайней мере, ничего такого, чего нельзя было бы достичь другим способом.)

...