[英]query on mysql table creation composite primary key from foreign keys of two tables
[英]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 TABLE
和EXPLAIN SELECT ...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.