[英]How do I identify non-linear increase in time for MySQL query?
以下是我正在運行的兩個查詢。 一種需要 75-80 秒,另一種需要 1.0-1.5 秒。 這兩個結果都顯示了預期的 50 行channel_administrators.channel_partner_id
。 更快和更慢查詢之間的區別在於SELECT
從登錄表中選擇唯一登錄。 登錄表有 460833 行,我知道這會減慢查詢速度。 我覺得這是意外的原因是單獨運行這段代碼時,上一個channel_administrators.channel_partner_id
結果回來在約0.2至0.7秒,最大channel_administrators.channel_partner_id
和50個結果,我不會期望它會超過50-秒。
我希望時間增加在最壞的情況下是線性的,但時間增加似乎不止於此。 這種非線性增加讓我覺得我做錯了什么(非常?),但我不知道如何找出我的查詢出了什么問題。 誰能告訴我為什么在這個查詢中有非線性時間增加?
我在帖子底部包含了一些我運行過的測試查詢及其最新時間。
編輯:我認為這種現象的最佳示例是查看測試 2 和測試 3。這些示例被盡可能地精簡,它顯示了運行邏輯一旦運行很快,但 50 次運行非常緩慢。
編輯 2:我添加了更多數據,在 6.93 秒而不是 75+ 秒內獲得相同的結果。 對於我的系統,我認為這是一個可以接受的結果。 我現在將寫下這個問題的答案。
80 秒查詢:
SELECT
info.managed_id,
info.channel_name,
info.registered_users,
info.new_users,
info.active_users,
info.coupon_opens
FROM channel_administrators
LEFT JOIN (
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
) AS registered_users,
(
SELECT COUNT(DISTINCT users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
AND users.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS new_users,
(
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = channel_partners.id
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS active_users,
(
SELECT COUNT(coupon_trackings.id) AS coupon_view_count
FROM coupon_trackings
WHERE coupon_trackings.channel_partner_id = channel_partners.id
AND coupon_trackings.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS coupon_opens
FROM channel_partners
) AS info
ON managed_id = channel_administrators.channel_partner_id
WHERE channel_administrators.user_id = 54184
ORDER BY info.channel_name
1.5 秒查詢(注釋掉差異):
SELECT
info.managed_id,
info.channel_name,
info.registered_users,
info.new_users,
-- info.active_users,
info.coupon_opens
FROM channel_administrators
LEFT JOIN (
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
) AS registered_users,
(
SELECT COUNT(DISTINCT users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
AND users.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS new_users,
-- (
-- SELECT COUNT(DISTINCT logins.user_id)
-- FROM logins
-- WHERE logins.channel_partner_id = channel_partners.id
-- AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
-- ) AS active_users,
(
SELECT COUNT(coupon_trackings.id) AS coupon_view_count
FROM coupon_trackings
WHERE coupon_trackings.channel_partner_id = channel_partners.id
AND coupon_trackings.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS coupon_opens
FROM channel_partners
) AS info
ON managed_id = channel_administrators.channel_partner_id
WHERE channel_administrators.user_id = 54184
ORDER BY info.channel_name
下面是我用來測試具有最大結果的通道上的單個時間的查詢。
測試 1:0.441s - 對於單個最大通道:
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
) AS registered_users,
(
SELECT COUNT(DISTINCT users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
AND users.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS new_users,
(
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = channel_partners.id
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS active_users,
(
SELECT COUNT(coupon_trackings.id) AS coupon_view_count
FROM coupon_trackings
WHERE coupon_trackings.channel_partner_id = channel_partners.id
AND coupon_trackings.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS coupon_opens
FROM channel_partners
WHERE channel_partners.id = 3255770
測試 2:0.368s - 最大頻道的活躍用戶:
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = 3255770
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
測試3:75.2s 只是登錄信息
SELECT
info.managed_id,
info.channel_name,
info.active_users
FROM channel_administrators
LEFT JOIN (
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = channel_partners.id
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS active_users
FROM channel_partners
) AS info
ON info.managed_id = channel_administrators.channel_partner_id
WHERE channel_administrators.user_id = 54184
測試 4:6.93 秒 - 通過重寫取得進展
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
) AS registered_users,
(
SELECT COUNT(DISTINCT users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
AND users.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS new_users,
(
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = channel_partners.id
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS active_users,
(
SELECT COUNT(coupon_trackings.id) AS coupon_view_count
FROM coupon_trackings
WHERE coupon_trackings.channel_partner_id = channel_partners.id
AND coupon_trackings.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS coupon_opens
FROM channel_partners
WHERE (channel_partners.id IN (SELECT
channel_administrators.channel_partner_id
FROM channel_administrators
WHERE channel_administrators.user_id = 54184
)
)
編輯:添加查詢結果(刪除名稱)
80年代查詢:
|managed_id|registered_users|new_users|active_users|coupon_opens|
|----------|----------------|---------|------------|------------|
|14 |1146 |46 |282 |893 |
|27 |2159 |48 |206 |635 |
|15 |2039 |68 |490 |2560 |
|16 |15 |0 |1 |0 |
|20 |1391 |53 |413 |1614 |
|21 |3 |0 |0 |0 |
|43 |1051 |36 |255 |1234 |
|44 |706 |19 |85 |276 |
|46 |16 |0 |4 |8 |
|47 |68 |1 |5 |30 |
|48 |169 |6 |40 |308 |
|49 |408 |13 |118 |434 |
|52 |52 |1 |11 |54 |
|53 |378 |11 |111 |391 |
|54 |34 |1 |5 |57 |
|75 |576 |7 |59 |145 |
|3255347 |773 |12 |99 |167 |
|685131 |142 |0 |9 |91 |
|76 |22 |0 |9 |25 |
|55 |276 |5 |68 |251 |
|56 |2232 |79 |534 |1644 |
|57 |78 |0 |10 |47 |
|58 |708 |10 |109 |364 |
|59 |1274 |42 |465 |1929 |
|60 |133 |0 |37 |97 |
|3 |0 |0 |127 |257 |
|2144749 |0 |0 |4 |40 |
|61 |629 |9 |119 |363 |
|63 |857 |36 |267 |892 |
|64 |49 |1 |13 |21 |
|65 |723 |15 |281 |1152 |
|66 |77 |0 |17 |48 |
|67 |123 |10 |59 |190 |
|68 |693 |8 |191 |387 |
|70 |80 |0 |31 |58 |
|71 |214 |1 |41 |102 |
|72 |104 |2 |23 |49 |
|3255770 |3149 |86 |542 |2280 |
|3255771 |3012 |39 |526 |2056 |
|77 |180 |9 |89 |239 |
|477 |677 |5 |286 |583 |
|478 |335 |191 |235 |2226 |
|479 |162 |12 |51 |159 |
|480 |57 |0 |8 |12 |
|302 |51 |3 |17 |32 |
|303 |213 |37 |116 |598 |
|373109 |9 |3 |6 |4 |
|373110 |10 |2 |5 |0 |
|373111 |29 |9 |16 |29 |
|3255810 |0 |0 |0 |0 |
2s查詢:
|managed_id|registered_users|new_users|coupon_opens|
|----------|----------------|---------|------------|
|14 |1146 |46 |893 |
|27 |2159 |48 |635 |
|15 |2039 |68 |2560 |
|16 |15 |0 |0 |
|20 |1391 |53 |1614 |
|21 |3 |0 |0 |
|43 |1051 |36 |1234 |
|44 |706 |19 |276 |
|46 |16 |0 |8 |
|47 |68 |1 |30 |
|48 |169 |6 |308 |
|49 |408 |13 |434 |
|52 |52 |1 |54 |
|53 |378 |11 |391 |
|54 |34 |1 |57 |
|75 |576 |7 |145 |
|3255347 |773 |12 |167 |
|685131 |142 |0 |91 |
|76 |22 |0 |25 |
|55 |276 |5 |251 |
|56 |2232 |79 |1644 |
|57 |78 |0 |47 |
|58 |708 |10 |364 |
|59 |1274 |42 |1929 |
|60 |133 |0 |97 |
|3 |0 |0 |257 |
|2144749 |0 |0 |40 |
|61 |629 |9 |363 |
|63 |857 |36 |892 |
|64 |49 |1 |21 |
|65 |723 |15 |1152 |
|66 |77 |0 |48 |
|67 |123 |10 |190 |
|68 |693 |8 |387 |
|70 |80 |0 |58 |
|71 |214 |1 |102 |
|72 |104 |2 |49 |
|3255770 |3149 |86 |2280 |
|3255771 |3012 |39 |2056 |
|77 |180 |9 |239 |
|477 |677 |5 |583 |
|478 |335 |191 |2226 |
|479 |162 |12 |159 |
|480 |57 |0 |12 |
|302 |51 |3 |32 |
|303 |213 |37 |598 |
|373109 |9 |3 |4 |
|373110 |10 |2 |0 |
|373111 |29 |9 |29 |
|3255810 |0 |0 |0 |
每個表都需要此復合索引,其中的列按給定順序排列:
INDEX(channel_partner_id, created_at)
如果您有這樣的索引,則僅在channel_partner_id
上刪除相應的索引。
將此“覆蓋”索引添加到channel_administrators
INDEX(user_id, channel_partner_id)
並刪除INDEX(user_id)
如果它存在。
通過不嵌套選擇來簡化查詢:
SELECT cp.id AS managed_id,
cp.name as channel_name,
( SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = cp.id
) AS registered_users,
((etc))
FROM channel_partners AS cp
JOIN channel_administrators AS ca
ON cp.managed_id = ca.channel_partner_id
WHERE ca.user_id = 54184
ORDER BY channel_name
提示:考慮改變
created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
到
created_at >= '2021-06-03'
AND created_at < '2021-06-03' + INTERVAL 30 DAY
或者如果更合適,使用+ INTERVAL 1 MONTH
。
如果您仍然有性能問題,讓我們看看查詢的樣子並提供SHOW CREATE TABLE
。
我仍然不知道為什么這會運行緩慢,但我找到了一個提高速度的解決方案,並將顯示我如何到達那里的步驟。
首先,在原始問題的測試 2 2 和測試 3 中,您可以看到對登錄表的單個查詢非常快,但是當查詢多個channel_partner_id
,整個查詢變得非常慢。 我懷疑這與我如何根據登錄表檢查channel_partner_id
有關。
我重寫了查詢以從列表而不是從選擇中獲取channel_partner_id
。 最終結果如下所示:
SELECT
channel_partners.id AS managed_id,
channel_partners.name as channel_name,
(
SELECT COUNT(users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
) AS registered_users,
(
SELECT COUNT(DISTINCT users.id)
FROM users
WHERE users.channel_partner_id = channel_partners.id
AND users.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS new_users,
(
SELECT COUNT(DISTINCT logins.user_id)
FROM logins
WHERE logins.channel_partner_id = channel_partners.id
AND logins.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS active_users,
(
SELECT COUNT(coupon_trackings.id) AS coupon_view_count
FROM coupon_trackings
WHERE coupon_trackings.channel_partner_id = channel_partners.id
AND coupon_trackings.created_at BETWEEN '2021-06-03' AND '2021-07-03' -- 30 days
) AS coupon_opens
FROM channel_partners
WHERE (channel_partners.id IN (SELECT
channel_administrators.channel_partner_id
FROM channel_administrators
WHERE channel_administrators.user_id = 54184
)
)
這個查詢花費了 6.93 秒,這仍然很慢,但它更接近我預期查詢所需的時間。
我無法解釋為什么一種方式更快,一種方式更慢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.