[慢查優化]聯表查詢注意誰是驅動表 & 你搞不清楚誰join誰更好時請放手讓mysql自行判定
阿新 • • 發佈:2018-12-30
夠複雜吧。Nested Loop Join 就是這樣,
以驅動表的結果集作為迴圈的基礎資料,然後將結果集中的資料作為過濾條件一條條地到下一個表中查詢資料,最後合併結果;此時還有第三個表,則將前兩個表的 Join 結果集作為迴圈基礎資料,再一次通過迴圈查詢條件到第三個表中查詢資料,如此反覆。
這條語句的執行計劃如下:
id select_type table type possible_keys key key_len ref rows Extra
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------- --------------------------------------------
1 SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index 由於動用了“LEFT JOIN”,所以攻城獅已經指定了驅動表,雖然這張驅動表的結果集記錄數達到百萬級! . . 如何優化? . . 優化第一步:LEFT JOIN改為JOIN
幹嘛要 left join 啊?直接 join!
立竿見影,驅動表立刻變為小表 mbei 了, Using temporary 消失了,影響行數少多了:
id select_type table type possible_keys key key_len ref rows Extra
------ ----------- ------ ------ -------------- ------- ------- ---------------------------- ------ --------------
1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13383 Using filesort
1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第一步之分支1:根據驅動表的欄位排序,好嗎? left join不變。幹嘛要根據非驅動表的欄位排序呢?我們前面說過“對驅動表可以直接排序,對非驅動表(的欄位排序)需要對迴圈查詢的合併結果(臨時表)進行排序!”的。
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------ -----------
1 SIMPLE mb index userid PRIMARY 4 (NULL) 10
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第二步:去除所有JOIN,讓MySQL自行決定! 寫這麼多密密麻麻的 left join/inner join 很開心嗎?
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------- --------------------------------------------
1 SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index 由於動用了“LEFT JOIN”,所以攻城獅已經指定了驅動表,雖然這張驅動表的結果集記錄數達到百萬級! . . 如何優化? . . 優化第一步:LEFT JOIN改為JOIN
explain SELECT mb.id……
FROM mb JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid WHERE 1=1 ORDER BY mbei.apply_time DESC limit 0,10
------ ----------- ------ ------ -------------- ------- ------- ---------------------------- ------ --------------
1 SIMPLE mbei
1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第一步之分支1:根據驅動表的欄位排序,好嗎? left join不變。幹嘛要根據非驅動表的欄位排序呢?我們前面說過“對驅動表可以直接排序,對非驅動表(的欄位排序)需要對迴圈查詢的合併結果(臨時表)進行排序!”的。
explain SELECT mb.id……也滿足業務場景,做到了rows最小: id select_type table type possible_keys key key_len ref rows Extra
FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid WHERE 1=1 ORDER BY mb.id DESC limit 0,10
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------ -----------
1 SIMPLE mb index userid PRIMARY 4 (NULL) 10
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第二步:去除所有JOIN,讓MySQL自行決定! 寫這麼多密密麻麻的 left join/inner join 很開心嗎?
explain SELECT mb.id……立竿見影,驅動表一樣是小表 mbei:
FROM mb,mbei,u
WHERE
mb.id=mbei.mb_id
and mb.uid=u.user_id
order by mbei.apply_time desc
limit 0,10