Как оптимизировать запрос mysql, используя более 20 миллионов записей - PullRequest
0 голосов
/ 25 марта 2020

Я использую MySQL для своего проекта, и у меня более 20 миллионов записей в таблице "mixpanel_data".

Так что, когда я пытаюсь получить записи за последние 6 месяцев, это прерывает запрос. Он предоставляет мне только записи последних 5-10 дней

Я использую следующий запрос MySQL.

SELECT  `sb_users`.`id`,`sb_users`.`name`, SUM(`mixpanel_data`.duration) as timeCount,
        COUNT(`mixpanel_data`.spread_id) as PageCount,`mixpanel_data`.`language`,
        `mixpanel_data`.`created_at`, `mixpanel_data`.`book_name`,
        `mixpanel_data`.`email`, `mixpanel_data`.`ip_address`,
        `mixpanel_data`.`event_date`, `mixpanel_data`.`type`,
        'Read', `mixpanel_data`.`unique_session_id`, `mixpanel_data`.`operating_system`,
        `mixpanel_data`.`country`, `mixpanel_data`.`region`, `mixpanel_data`.`city`,
        `mixpanel_data`.`device`, `mixpanel_data`.`browser`,
        `mixpanel_data`.`browser_version`
    FROM  `mixpanel_data`
    LEFT JOIN  `sb_users`  ON `mixpanel_data`.`first_name` = `sb_users`.`username`
    WHERE  `mixpanel_data`.`email` !=''
      AND  `mixpanel_data`.`created_at` Between '2019-03-24' AND '2020-03-24'
      and  `mixpanel_data`.`action` IN('PauseAudio')
    GROUP BY  `mixpanel_data`.`email`, `mixpanel_data`.`book_name` ,
        `mixpanel_data`.`language`
 UNION 
 SELECT  `sb_users`.`id`,`sb_users`.`name`, SUM(`mixpanel_data`.duration) as timeCount,
        COUNT(`mixpanel_data`.spread_id) as PageCount,`mixpanel_data`.`language`,
        `mixpanel_data`.`created_at`, `mixpanel_data`.`book_name`,
        `mixpanel_data`.`email`, `mixpanel_data`.`ip_address`,
        `mixpanel_data`.`event_date`, `mixpanel_data`.`type`,
        'Read', `mixpanel_data`.`unique_session_id`, `mixpanel_data`.`operating_system`,
        `mixpanel_data`.`country`, `mixpanel_data`.`region`, `mixpanel_data`.`city`,
        `mixpanel_data`.`device`, `mixpanel_data`.`browser`,
        `mixpanel_data`.`browser_version`
    FROM  `mixpanel_data`
    LEFT JOIN  `sb_users`  ON `mixpanel_data`.`first_name` = `sb_users`.`username`
    WHERE  `mixpanel_data`.`email` !=''
      AND  `mixpanel_data`.`created_at` Between '2019-03-24' AND '2020-03-24'
      and  `mixpanel_data`.`action` NOT IN('PlayAudio','PauseAudio')
      AND  `mixpanel_data`.`spread_id` !=''
    GROUP BY  `mixpanel_data`.`email`, `mixpanel_data`.`book_name` ,
        `mixpanel_data`.`language`

Я пытался изменить свой запрос следующим запросом, но это не работает для меня. Это также нарушает запрос и также дает мне меньше записей.

SELECT sb_users.id,
sb_users.NAME,
Count(mixpanel_data.spread_id) AS PageCount,
SUM(CASE When action IN ('PauseAudio') Then duration Else 0 End) as total, SUM(CASE When action NOT IN ('PlayAudio', 'PauseAudio') Then duration Else 0 End) as Sectotal,
mixpanel_data.language,
mixpanel_data.created_at,
mixpanel_data.book_name,
mixpanel_data.email,
mixpanel_data.ip_address,
mixpanel_data.event_date,
mixpanel_data.type,
'Read',
mixpanel_data.unique_session_id,
mixpanel_data.operating_system,
mixpanel_data.country,
mixpanel_data.region,
mixpanel_data.city,
mixpanel_data.device,
mixpanel_data.browser,
mixpanel_data.browser_version
FROM `mixpanel_data`
LEFT JOIN sb_users
ON `mixpanel_data`.`first_name` = `sb_users`. `username`
WHERE
mixpanel_data.email != '' AND mixpanel_data.`created_at` Between '2019-03-24' AND '2020-03-24'
AND `mixpanel_data`.`spread_id` !='' GROUP BY mixpanel_data.email,
mixpanel_data.book_name,
mixpanel_data.language

Я также попытался добавить индексатор для столбца first_name, username и made_at. Но запрос занимает много времени (более 15-16 секунд)

Может ли кто-нибудь помочь мне оптимизировать запрос?

1 Ответ

0 голосов
/ 25 марта 2020

Этот индекс на sb_users может помочь: INDEX(username, name, id).

Переключение с UNION на UNION ALL должно ускорить запрос. Но это может привести к дублированию строк.

Вы получаете данные за год; какой процент таблицы это? Если это большой процент, то индекс mixpanel_data не будет полезен.

GROUP BY может быть неуместным, если у вас нет UNIQUE(book_name, email, language). Что, если пользователь смотрит на одну и ту же книгу с двух разных ip_addresses; какой ip_address будет доставлен по запросу?

Если предположить, что единственные различия между SELECTs являются

      and  ma.`action` IN('PauseAudio')

против

      and  ma.`action` NOT IN('PlayAudio','PauseAudio')
      AND  ma.`spread_id` !=''

, рассмотрите следующее - если вы используете MySQL 8.0:

WITH cte AS
        SELECT ...
            FROM  `mixpanel_data` AS ma
            LEFT JOIN  `sb_users` AS su
               ON ma.`first_name` = su.`username`
            WHERE  ma.`email` !=''
              AND  ma.`created_at` >= '2019-03-24'
              AND  ma.`created_at`  < '2019-03-24' + INTERVAL 1 YEAR
SELECT * FROM cte
      WHERE `action` IN('PauseAudio')
UNION ALL
SELECT * FROM cte
      WHERE `action` NOT IN('PlayAudio','PauseAudio')
        AND `spread_id` !=''
GROUP BY  `email`, `book_name` , `language`

Надежда состоит в том, что это приводит к сканированию mixpanel_data только один раз.

Другой подход (и не зависит на 8.0): иметь внутренний запрос («производная таблица»), который кодирует работу с mixpanel_data, , затем JOIN до sb_users:

 SELECT ...
     FROM ( SELECT ...
               FROM mixpanel_data
               -- (no JOIN)
               WHERE ...
               GROUP BY ...
            UNION ALL
               FROM mixpanel_data
               -- (no JOIN)
               WHERE ... (the other)
               GROUP BY ...
          )
    LEFT JOIN sb_users ON ...

Преимущество это то, что он не перемещается вокруг данных sb_user на протяжении всей работы. Вместо этого он извлекается из sb_user после сокращения количества строк с помощью GROUP BY.

Может быть больше подсказок; пожалуйста, сделайте некоторые из вышеперечисленных, затем укажите EXPLAIN SELECT и некоторые размеры таблиц.

Это был первый запрос. второй запрос отличается по двум причинам:

  • тест spread_id отсутствует
  • может привести к сбросу UNION к другому набору строк.

Мое предложение о JOINing до sb_users после выполнения GROUP BY применимо и здесь (хотя без UNION) .

Отладка

  1. SELECT ... FROM mixpanel_data без SUM, COUNT и GROUP BY, но с добавлением LIMIT. Посмотрите, выглядят ли данные так, как вы ожидаете.
  2. Добавьте в SUM, COUNT и GROUP BY; Посмотрите на эти результаты.
  3. Затем JOIN на другой стол.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...