不要求每個人一定了解 聯表查詢(join/left join/inner join等)時的mysql運算過程;
不要求每個人一定知道線上(現在或未來)哪張表資料量大,哪張表資料量小;
但要經常使用explain檢視執行計劃,這是一種美德!
下面兩個查詢,它們隻差了一個order by,效果卻迥然不同。
第一個查詢:
執行計劃為:
第二個查詢:
執行計劃裡沒有了using temporary:
DBA告訴我們,MySQL 表關聯的算法是 Nest Loop Join,是通過驅動表的結果集作為循環基礎資料,然後一條一條地通過該結果集中的資料作為過濾條件到下一個表中查詢資料,然後合并結果。
EXPLAIN 結果中,第一行出現的表就是驅動表(Important!)
以上兩個查詢語句,驅動表都是 city,如上面的執行計劃所示!
對驅動表可以直接排序,對非驅動表(的字段排序)需要對循環查詢的合并結果(臨時表)進行排序(Important!)
是以,order by ads.id desc 時,就要先 using temporary 了!
當進行多表連接配接查詢時, 驅動表的定義為:
1)指定了聯接條件時,滿足查詢條件的記錄行數少的表為驅動表;
2)未指定聯接條件時,行數少的表為驅動表(Important!)。
忠告:如果你搞不清楚該讓誰做驅動表、誰 join 誰,請讓 MySQL 運作時自行判斷
既然未指定聯接條件時,行數少的表為驅動表了,而且你也對自己寫出的複雜的 Nested Loop Join 不太有把握(如下面的執行個體所示),那就别指定誰 left/right join 誰了,請交給 MySQL優化器 運作時決定吧。
我們優化的目标是盡可能減少JOIN中Nested Loop的循環次數,以此保證:永遠用小結果集驅動大結果集(Important!)!
先了解一下 mb 表有 千萬級記錄,mbei 表要少得多。慢查執行個體如下:
夠複雜吧。Nested Loop Join 就是這樣,以驅動表的結果集作為循環的基礎資料,然後将結果集中的資料作為過濾條件一條條地到下一個表中查詢資料,最後合并結果;此時還有第三個表,則将前兩個表的 Join 結果集作為循環基礎資料,再一次通過循環查詢條件到第三個表中查詢資料,如此反複。
這條語句的執行計劃如下:
由于動用了“LEFT JOIN”,是以攻城獅已經指定了驅動表,雖然這張驅動表的結果集記錄數達到百萬級!
幹嘛要 left join 啊?直接 join!
立竿見影,驅動表立刻變為小表 mbei 了, Using temporary 消失了,影響行數少多了:
left join不變。幹嘛要根據非驅動表的字段排序呢?我們前面說過“對驅動表可以直接排序,對非驅動表(的字段排序)需要對循環查詢的合并結果(臨時表)進行排序!”的。
也滿足業務場景,做到了rows最小:
寫這麼多密密麻麻的 left join/inner join 很開心嗎?
立竿見影,驅動表一樣是小表 mbei:
不要過于相信你的運氣!
不要相信你的開發環境裡SQL的執行速度!
請拿起 explain 武器,
如果你看到以下現象,請優化:
出現了Using temporary;
rows過多,或者幾乎是全表的記錄數;
key 是 (NULL);
possible_keys 出現過多(待選)索引。