Как я могу оптимизировать мой запрос MySQL? поскольку для извлечения записей требуется 45 секунд

#mysql #query-optimization

#mysql #запрос-оптимизация

Вопрос:

В моей таблице базы данных «ringtune_history» содержится 40 тысяч записей, а в «ringtunes» — 4 тысячи записей, и обработка запроса занимает слишком много времени.

 SELECT  `ringtunes`.*,
        count(case when ringtune_history.Action = 'Download' then ringtune_history.Action end) as Org_Downloads,
        count(case when ringtune_history.Action = 'View' then ringtune_history.Action end) as Org_Views,
        count(case when ringtune_history.Action = 'Play' then ringtune_history.Action end) as Total_Plays,
        count(case when ringtune_history.Action = 'Like' then ringtune_history.Action end) as Total_Likes,
        `categories`.`Name` as `Category_Name`
    FROM  `ringtunes`
    LEFT JOIN  `ringtune_history`  ON `ringtune_history`.`Ringtune_Id` = `ringtunes`.`Id`
    LEFT JOIN  `categories`  ON `categories`.`Id` = `ringtunes`.`Category`
    WHERE  `ringtunes`.`Deleted` = 0
      AND  `ringtunes`.`Status` = 1
      AND  `categories`.`Deleted` = 0
      AND  `categories`.`Status` = 1
    GROUP BY  `ringtunes`.`Id`
    ORDER BY  `ringtunes`.`Id` DESC
    LIMIT  20
  

Комментарии:

1. Вам нужно добавить индексы в вашу таблицу, создать индекс для всех таблиц. Для создания индексов используйте столбцы, которые вы указываете в предложении WHERE.

2. Очень полезно, спасибо!

Ответ №1:

Ваш запрос в порядке! Проблема в самой таблице.

Обязательно добавьте индексы к столбцам, которые вы используете для поиска записей (например. ringtunes.Deleted = 0 , ваш индекс должен будет перейти к удаленному столбцу). То же самое касается других столбцов, которые вы проверяете на наличие значений.

https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html

Ответ №2:

Не используйте LEFT , если вам не нужны нули, где отсутствует «правильная» таблица. Очевидно, что categories этого не должно быть LEFT . Неясно, ringtune_history можно ли изменить на обычный JOIN .

Необходимы индексы:

 ringtune:  (deleted, status, ID) or (status, deleted, ID)
ringtune_history:  (Ringtune_Id, Action)
  

(Пожалуйста, предоставьте SHOW CREATE TABLE на случай, если есть другие подсказки.)

Похоже, у вас есть шаблон «explode-implode» — сначала вы JOIN используете несколько таблиц, что приводит к потенциально большему набору промежуточных строк. При этом эта таблица довольно громоздкая из-за большого количества столбцов. Затем вы сворачиваете все с помощью GROUP BY . Если возможно, сначала найдите идентификаторы таблицы, которую вы в конечном итоге GROUP BY получите. Затем JOIN это в другие таблицы и (вероятно) обратно в исходную таблицу — для получения остальных столбцов.

Вы можете, по крайней мере, отложить просмотр ringtune_history до тех пор, пока не найдете 20 нужных строк в двух других таблицах. Это ограничит количество поисковых запросов в нем только 20, а не «всеми».

Если deleted и status из двух столбцов всегда совпадают, то не проверяйте их categories . (Это упростит достижение исправления explode-implode.) Если они обязательно независимы, то я полагаю, что для каждой таблицы было бы лучше, если бы вы действительно удалили нежелательные строки (или переместили их в таблицу архива). Текущее WHERE предложение является сложным, поскольку оно фильтрует две разные таблицы.

(Не критично:) Это

 count(case when ringtune_history.Action = 'View' then ringtune_history.Action end)
  

может быть сокращен до

 SUM(ringtune_history.Action = 'View')