簡體   English   中英

在主/外鍵上連接兩個表時,MySQL的查詢速度緩慢

[英]Painfully slow MySql query when joining two tables on primary/foreign keys

我們使用Ahoy紅寶石庫來跟蹤用戶訪問和事件。 為了向用戶提供反饋,我們會定期對某些事件和訪問進行計數。

這兩個表相對較大,但並不龐大。 訪問次數為6MM +行,事件為23MM +行。

下面是一個示例查詢,需要80秒鍾才能運行:

SELECT COUNT(*) 
FROM `ahoy_events` 
INNER JOIN `visits`  ON `visits`.`id` = `ahoy_events`.`visit_id` 
WHERE `ahoy_events`.`event_target_id` = 8471 
  AND `ahoy_events`.`event_target_type` = 'Project' 
  AND visits.entity_id = 668 
  AND (`visits`.`user_type` IS NULL OR `visits`.`user_type` = 'User')

這是該查詢的解釋:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: visits
   partitions: NULL
         type: ref
possible_keys: PRIMARY,index_visits_on_entity_id,index_visits_on_entity_id_and_user_type,index_visits_on_entity_id_and_started_at,index_visits_on_entity_id_and_user_id_and_user_type,index_visits_on_entity_id_user_id_user_type_started_at
          key: index_visits_on_entity_id_user_id_user_type_started_at
      key_len: 5
          ref: const
         rows: 1567140
     filtered: 19.00
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: ahoy_events
   partitions: NULL
         type: ref
possible_keys: index_ahoy_events_on_visit_id,index_ahoy_events_on_event_target_id_and_event_target_type
          key: index_ahoy_events_on_visit_id
      key_len: 17
          ref: givecorpssite.visits.id
         rows: 2
     filtered: 11.47
        Extra: Using where

當我僅對單個表進行計數時,每個表的運行時間為200毫秒至600毫秒,即:

SELECT count(*) FROM `ahoy_events` WHERE `ahoy_events`.`event_target_id` = 8471 AND `ahoy_events`.`event_target_type` = 'Project'

SELECT count(*) FROM `visits` where visits.entity_id = 668 AND (`visits`.`user_type` IS NULL OR `visits`.`user_type` = 'Donor')

但是將它們連接到主/外鍵上,會使查詢花費80s +

順便說一句,鍵(visit_id和訪問時的id列)是UUID,並且是BINARY(16)列。

我相信這個查詢不會那么慢是我錯了嗎?

由於目前尚不清楚OR選擇條件是否會導致問題,並且您並沒有真正在結果中尋找行級數據,因此您可以嘗試這種條件聚合:

SELECT COUNT(IF(`visits`.`user_type` IS NULL OR `visits`.`user_type` = 'User',1,NULL) 
FROM `ahoy_events` 
INNER JOIN `visits`  ON `visits`.`id` = `ahoy_events`.`visit_id` 
WHERE `ahoy_events`.`event_target_id` = 8471 
  AND `ahoy_events`.`event_target_type` = 'Project' 
  AND visits.entity_id = 668 
;

COUNT忽略空值; 或者, SUM(IF(visits.user_type IS NULL OR visits.user_type = 'User',1,0))更加清晰,並且得到相同的結果(盡管從理論上講,在性能方面可能會稍微高一些)。

在此查詢中,您將處理更多的行而不會減少條件,但與掃描表中較小的結果集相比,它可能最終“便宜”以掃描較大的結果。

覆蓋指數:

visits:  INDEX(entity_id, user_type, id)  -- in this order
ahoy_events:  INDEX(event_target_id, event_target_type, visit_id)

通過覆蓋,可能會減少I / O。 (I / O是查詢中最慢的部分。)

有可能以下程序會運行得更快:

SELECT  
    (
        SELECT  COUNT(*)
            FROM  `ahoy_events` AS e
            INNER JOIN  `visits` AS v  ON v.`id` = e.`visit_id`
            WHERE  e.`event_target_id` = 8471
              AND  e.`event_target_type` = 'Project'
              AND  visits.entity_id = 668
              AND  v.`user_type` IS NULL 
    ) + 
    (
        SELECT  COUNT(*)
            FROM  `ahoy_events` AS e
            INNER JOIN  `visits` AS v  ON v.`id` = e.`visit_id`
            WHERE  e.`event_target_id` = 8471
              AND  e.`event_target_type` = 'Project'
              AND  visits.entity_id = 668
              AND  v.`user_type` = 'User' 
    );

它需要我上面建議的相同索引。

這里的理由是避免OR (索引通常不能與OR一起使用。)

如果您想進一步討論,請提供SHOW CREATE TABLEEXPLAIN SELECT ...

暫無
暫無

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

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