我試圖檢索分頁列表和總數「關於屬於特定用戶的」案例「的」通知「。在JOERE中使用OR緩慢JOIN查詢 - 缺少可能的索引?
通知有幾個條件爲「未鎖定」,「不是私人」,「尚未見過」,應該返回#找到,然後按照創建的日期降序排列。
最後一個條件是,該通知不是由用戶本身產生,或該通知的類型是「行爲」(枚舉)和user_id是在通知中涉及「REF_ID」
此查詢正在接近5秒鐘,以對最近的變化中的200k行和在cases
和50個用戶中少於4k行進行運行。
+-----+
| cnt |
+-----+
| 13 |
+-----+
1 row in set (4.67 sec)
該查詢是否可以自行優化,還是需要重構?
SELECT count(*) as cnt
FROM recent_changes rc
LEFT JOIN `case` c on c.id = rc.case_id
LEFT JOIN `user` u on u.id = rc.user_id
WHERE
(
rc.user_id != c.user_id AND c.user_id = '25'
OR
(rc.type = 'conduct' AND rc.ref_id = '25')
)
AND c.locked = 'N' AND rc.private != 'Y'
AND seen = 'false'
ORDER BY rc.datecreated DESC;
Explain輸出上recent_changes
+----+-------------+-------+--------+--------------------------+-------------------------+---------+--------------------------+------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------+-------------------------+---------+--------------------------+------+------------------------------+
| 1 | SIMPLE | c | ALL | PRIMARY,user_user_id_idx | NULL | NULL | NULL | 3699 | Using where; Using temporary |
| 1 | SIMPLE | rc | ref | idx_recent_changes_case | idx_recent_changes_case | 5 | xxxxxxxxxxxxx.c.id | 25 | Using where |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | xxxxxxxxxxxxx.rc.user_id | 1 | Using index |
+----+-------------+-------+--------+--------------------------+-------------------------+---------+--------------------------+------+------------------------------+
指數:上case
表
+----------------+------------+------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------------+------------+------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| recent_changes | 0 | PRIMARY | 1 | id | A | 182807 | NULL | NULL | | BTREE | |
| recent_changes | 1 | recent_changes_user_id_idx | 1 | user_id | A | 96 | NULL | NULL | YES | BTREE | |
| recent_changes | 1 | idx_recent_changes_user_case | 1 | user_id | A | 92 | NULL | NULL | YES | BTREE | |
| recent_changes | 1 | idx_recent_changes_user_case | 2 | case_id | A | 18280 | NULL | NULL | YES | BTREE | |
| recent_changes | 1 | idx_recent_changes_case | 1 | case_id | A | 7312 | NULL | NULL | YES | BTREE | |
+----------------+------------+------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
指標:
+-------+------------+------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| case | 0 | PRIMARY | 1 | id | A | 3753 | NULL | NULL | | BTREE | |
| case | 1 | id_idx | 1 | member_id | A | 3753 | NULL | NULL | YES | BTREE | |
| case | 1 | user_user_id_idx | 1 | user_id | A | 2 | NULL | NULL | YES | BTREE | |
| case | 1 | case_ha_id | 1 | health_authority_id | A | 28 | NULL | NULL | YES | BTREE | |
+-------+------------+------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
它做以下概念:
在recent_changes查找最近的行,其中:
i)本recent_changes行通過由該電流的user_id II擁有CASE_ID加入到case
表)和recent_changes行不是由當前USER_ID創建
OR
i)所述recent_changes行是 「行爲」 類型的,並且當前的user_id是在recent_changes.ref_id柱
如果刪除了「OR( rc.type ='conduct'AND rc.ref_id ='25')「condition,那麼我得到1秒鐘的響應時間。
如果我刪除「rc.user_id!= c.user_id AND c.user_id ='25'或」條件,它仍然需要大約5秒才能完成。
編輯
改變連接順序剃掉1/2秒,雖然我不能在rc
.case_id加入case
直到我加入rc
第一:未知列「rc.user_id '在'where子句'中。
新建查詢:
SELECT count(*) as cnt
FROM `user` u
LEFT JOIN `recent_changes` rc on u.id = rc.user_id
LEFT JOIN `case` c on c.id = rc.case_id
WHERE
(
rc.user_id != c.user_id AND c.user_id = '25'
OR
(rc.type = 'conduct' AND rc.ref_id = '25')
)
AND c.locked = 'N' AND rc.private != 'Y'
AND seen = 'false'
ORDER BY rc.datecreated DESC;
取出「ORDER BY」條款似乎並沒有增加新的連接順序查詢,雖然我現在是更好地瞭解它的性能影響。
使用UNION沒有任何速度快,但運行的每個單獨選擇已指出,首隻選擇需要.3s其中第二選擇是在4S:
select count(*) as cnt
FROM (
SELECT count(*) FROM `user` u
LEFT JOIN `recent_changes` rc on u.id = rc.user_id
LEFT JOIN `case` c on c.id = rc.case_id
WHERE rc.user_id != c.user_id AND c.user_id = '25'
AND c.locked = 'N' AND rc.private != 'Y'
AND seen = 'false'
UNION ALL
SELECT count(*) as cnt
FROM `user` u
LEFT JOIN `recent_changes` rc on u.id = rc.user_id
LEFT JOIN `case` c on c.id = rc.case_id
WHERE rc.type = 'conduct' AND rc.ref_id = '25'
AND c.locked = 'N' AND rc.private != 'Y'
AND seen = 'false') x
我相信recent_changes rc
表沒有按「T有必要的索引作爲每說明:
EXPLAIN SELECT count(*) FROM `user` u LEFT JOIN `recent_changes` rc on u.id = rc.user_id LEFT JOIN `case` c on c.id = rc.case_id WHERE rc.user_id != c.user_id AND c.user_id = '25' AND c.locked = 'N' AND rc.private != 'Y' AND seen = 'false';
在奔跑,5S <
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
| 1 | SIMPLE | c | ref | PRIMARY,user_user_id_idx | user_user_id_idx | 5 | const | 383 | Using where |
| 1 | SIMPLE | rc | ref | recent_changes_user_id_idx,idx_recent_changes_user_case,idx_recent_changes_case | idx_recent_changes_case | 5 | hsaedmp_jason.c.id | 20 | Using where |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | hsaedmp_jason.rc.user_id | 1 | Using index |
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
在奔跑> 4S
EXPLAIN SELECT count(*) as cnt FROM `user` u LEFT JOIN `recent_changes` rc on u.id = rc.user_id LEFT JOIN `case` c on c.id = rc.case_id WHERE rc.type = 'conduct' AND rc.ref_id = '25' AND c.locked = 'N' AND rc.private != 'Y' AND seen = 'false';
密鑰= NULL這是不好的。
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
| 1 | SIMPLE | c | ALL | PRIMARY | NULL | NULL | NULL | 3797 | Using where |
| 1 | SIMPLE | rc | ref | recent_changes_user_id_idx,idx_recent_changes_user_case,idx_recent_changes_case | idx_recent_changes_case | 5 | hsaedmp_jason.c.id | 20 | Using where |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | hsaedmp_jason.rc.user_id | 1 | Using index |
+----+-------------+-------+--------+---------------------------------------------------------------------------------+-------------------------+---------+--------------------------+------+-------------+
我感到困惑的是,解釋說,case
表不使用鑰匙節目,但現在看來,該recent_changes
表是需要對ref_id
列的索引的一個?
下面是該索引的解釋,在這裏看起來好多了,但我還沒有能夠在產品上測試它。
+----+-------------+-------+------------+--------+----------------------------------------------------------------------------------------------------------------------------------
---+------------------------+---------+--------------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys
| key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+----------------------------------------------------------------------------------------------------------------------------------
---+------------------------+---------+--------------------------+------+----------+-------------+
| 1 | SIMPLE | rc | NULL | ref | recent_changes_user_id_idx,idx_recent_changes_user_case,idx_recent_changes_case,idx_recent_changes_case_date,idx_recent_changes_r
ef | idx_recent_changes_ref | 5 | const | 2096 | 3.12 | Using where |
| 1 | SIMPLE | u | NULL | eq_ref | PRIMARY
| PRIMARY | 4 | hsaedmp_jason.rc.user_id | 1 | 100.00 | Using index |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY
| PRIMARY | 4 | hsaedmp_jason.rc.case_id | 1 | 50.00 | Using where |
+----+-------------+-------+------------+--------+----------------------------------------------------------------------------------------------------------------------------------
---+------------------------+---------+--------------------------+------+----------+-------------+
UPDATE
我已經返工使用UNION語句的查詢,改變連接順序和由上recent_changes
表添加化合物索引一起帶來的查詢響應時間< 10ms的。
這是使用UNION語句的新查詢。
select count(*) as num
FROM (
(
SELECT rc1.*
FROM `user` u1
LEFT JOIN `recent_changes` rc1 on u1.id = rc1.user_id
LEFT JOIN `case` c1 on c1.id = rc1.case_id
WHERE
(rc1.user_id != c1.user_id AND c1.user_id = '1')
AND c1.locked = 'Y'
AND rc1.private != 'Y'
AND seen = 'false'
ORDER BY rc1.datecreated DESC
)
UNION
(
SELECT rc.*
FROM `user` u
LEFT JOIN `recent_changes` rc on u.id = rc.user_id
LEFT JOIN `case` c on c.id = rc.case_id
WHERE
(rc.type = 'conduct' AND rc.ref_id = '1')
AND c.locked = 'Y'
AND rc.private != 'Y'
AND seen = 'false'
ORDER BY rc.datecreated DESC
)
) x;
而我根據最終查詢創建的索引是我需要的。
ALTER TABLE recent_changes ADD INDEX idx_recent_changes_notification (type, ref_id, private, seen, user_id);
感謝大家的意見!
'OR'是MySQL中的一個性能殺手。試着將它分成兩個查詢,你可以結合'UNION'。 – Barmar
另外,運行EXPLAIN EXTENDED,然後顯示SHOW WARNINGS。它會揭示一些關於MySQL如何解釋你的JOIN的有用信息 – Strawberry
嘗試在'recent_changes(type,ref_id,private,user_id)上創建一個複合索引'這是一個所謂的複合覆蓋索引,並且有助於加速UNION的第二部分。請[編輯]你的問題,讓我們知道它是否有幫助。嘗試修復性能問題時,在表中放置大量單列索引通常是有害的。 –