#mysql #sql #datetime #query-optimization #where-clause
#mysql #sql #дата и время #оптимизация запросов #where-предложение
Вопрос:
Я пытаюсь решить проблему с производительностью с помощью этой таблицы
-------------- ------------------ ------ ----- --------- ----------------
| Field | Type | Null | Key | Default | Extra |
-------------- ------------------ ------ ----- --------- ----------------
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| direction_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| rate | decimal(16,6) | NO | | NULL | |
-------------- ------------------ ------ ----- --------- ----------------
которая содержит около 100 миллионов строк
Существует только один запрос, который выбирает данные из этой таблицы:
SELECT AVG(rate) AS rate, created_at
FROM statistics
WHERE direction_id = ?
AND created_at BETWEEN ? AND ?
GROUP BY created_at
direction_id
это внешний ключ, но он обладает довольно низкой избирательностью:
---- ------------- ------------ ------------ ------ --------------------------------- --------------------------------- --------- ------- ------- ---------- ---------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---- ------------- ------------ ------------ ------ --------------------------------- --------------------------------- --------- ------- ------- ---------- ---------------------------------------------------------------------
| 1 | SIMPLE | statistics | NULL | ref | statistics_direction_id_foreign | statistics_direction_id_foreign | 4 | const | 26254 | 11.11 | Using index condition; Using where; Using temporary; Using filesort |
---- ------------- ------------ ------------ ------ --------------------------------- --------------------------------- --------- ------- ------- ---------- ---------------------------------------------------------------------
Итак, я ищу способ решить эту проблему и нуждаюсь в совете.
Помогает ли мне разделение по ХЭШУ (direction_id) или нет?
Если это поможет, каков наилучший способ сделать это?
Или, может быть, есть какой-то другой способ это исправить.
Комментарии:
1.
YEAR(created_at), MONTH(created_at), DAY(created_at)
А?2. @Strawberry это не имеет значения, я думаю, это можно было бы опустить
Ответ №1:
Вы имели в виду это для среднесуточных ставок?
SELECT AVG(rate) AS rate,
DATE(created_at)
FROM statistics
WHERE direction_id = ?
AND created_at BETWEEN ? AND ?
GROUP BY DATE(created_at)
И есть INDEX(direction_id, created, rate)
— это и «покрытие», и «составное». В объяснении будет указано «Использование индекса», чтобы указать «покрытие», что указывает на то, что весь запрос может быть выполнен, просматривая только BTree индекса. Следовательно, «Покрытие» дает дополнительный прирост производительности.
Переход на необычный индекс, включающий DATE(created_at)
, вероятно, не поможет в этом запросе.
PARTITIONing
не указано.
может быть указано «Сводные таблицы». http://mysql.rjweb.org/doc.php/summarytables
Комментарии:
1. Я решил уменьшить объем данных для каждого
direction_id
. Я думаю, что это единственный способ. Спасибо.
Ответ №2:
Во-первых, давайте исправим ваш запрос так, чтобы он был допустимым агрегирующим запросом. Предположительно, вам нужно среднее значение за день rate
, так что:
SELECT AVG(rate) AS rate, DATE(created_at) as created_day
FROM statistics
WHERE direction_id = ? AND created_at BETWEEN ? AND ?
GROUP BY DATE(created_at)
Затем я бы рекомендовал создать следующий индекс:
create index idx_statistics on statistics (direction_id, created_at, rate);
В последних версиях MySQL мы могли бы также рассмотреть возможность использования индекса on date(create_at)
. Если вы можете жить со следующим where
предложением:
WHERE direction_id = ? AND DATE(created_at) BETWEEN ? AND ?
Тогда пригодился бы следующий индекс:
create index idx_statistics on statistics (direction_id, (date(created_at)), rate);
Комментарии:
1. необходимо ли включать
rate
в индекс?2. В частности, MySQL 8.0.13 является первой версией, которая поддерживает индексы выражений.
3. кажется, что это не имеет никакого эффекта. Используется индекс, но по-прежнему остается 26200 строк
4. @ArtemIlchenko: Я не уверен в вашем комментарии. Индекс используется для повышения производительности, а не для изменения количества строк, возвращаемых запросом.
5. Индекс называется «покрывающим», потому что он включает все столбцы, необходимые в любом месте запроса. Объяснение укажет на это, сохранив «Использование индекса» (что не то же самое, что «Использование условия индекса»).