Оптимизация производительности больших таблиц MySQL

#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. Индекс называется «покрывающим», потому что он включает все столбцы, необходимые в любом месте запроса. Объяснение укажет на это, сохранив «Использование индекса» (что не то же самое, что «Использование условия индекса»).