簡體   English   中英

如何優化 mysql 查詢超過 2000 萬條記錄

[英]How to optimize the mysql query with more the 20 millions records

我在我的項目中使用 MySQL,並且在“mixpanel_data”表中有超過 2000 萬條記錄。

因此,當我嘗試獲取過去 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 和 created_at 列上添加索引器。 但是查詢需要很多時間(超過 15-16 秒)

有人可以幫我優化查詢嗎?

sb_users上的這個索引可能有幫助: INDEX(username, name, id)

UNION切換到UNION ALL應該會加快查詢速度。 但它可能會導致重復的行。

您正在獲取一年的數據; 那是桌子的百分之幾? 如果這是一個很大的百分比,那么mixpanel_data索引就沒有用了。

除非您有UNIQUE(book_name, email, language)否則GROUP BY可能不合適。 對於,如果用戶從兩個不同的 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 數據。 相反,它通過GROUP BY縮小行數從 sb_user 獲取。

可能還有更多提示; 請執行上述一些操作,然后提供EXPLAIN SELECT和一些表大小。

這是第一個查詢。 第二個查詢在幾個方面有所不同:

  • 缺少spread_id的測試
  • UNION的重復數據刪除可能會導致一組不同的行。

我對建議JOINingsb_usersGROUP BY在這里也適用(雖然沒有UNION )。

調試

  1. SELECT ... FROM mixpanel_data沒有SUMCOUNTGROUP BY ,但添加一個LIMIT 查看數據是否符合您的預期。
  2. 添加SUMCOUNTGROUP BY 關注那些結果。
  3. 然后JOIN到另一個表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM