今天遇到乙個left join優化的問題,搞了一下午,中間查了不少資料,對mysql的查詢計畫還有查詢優化有了更進一步的了解,做乙個簡單的記錄:
select c.* from hotel_info_original c
left join hotel_info_collection h
on c.hotel_type=h.hotel_type and c.hotel_id =h.hotel_id
where h.hotel_id is null
這個sql是用來查詢出c表中有h表中無的記錄,所以想到了用left join的特性(返回左邊全部記錄,右表不滿足匹配條件的記錄對應行返回null)來滿足需求,不料這個查詢非常慢。先來看查詢計畫:
rows代表這個步驟相對上一步結果的每一行需要掃瞄的行數,可以看到這個sql需要掃瞄的行數為35773*8134,非常大的乙個數字。本來c和h表的記錄條數分別為40000+和10000+,這幾乎是兩個表做笛卡爾積的開銷了(select * from c,h)。
於是我上網查了下mysql實現join的原理,原來mysql內部採用了一種叫做 nested loop join的演算法。nested loop join 實際上就是通過驅動表的結果集作為迴圈基礎資料,然後一條一條的通過該結果集中的資料作為過濾條件到下乙個表中查詢資料,然後合併結果。如果還有第三個參與 join,則再通過前兩個表的 join 結果集作為迴圈基礎資料,再一次通過迴圈查詢條件到第三個表中查詢資料,如此往復,基本上mysql採用的是最容易理解的演算法來實現join。所以驅動表的選擇非常重要,驅動表的資料小可以顯著降低掃瞄的行數。
那麼為什麼一般情況下join的效率要高於left join很多?很多人說不明白原因,只人云亦云,我今天下午感悟出來了一點。一般情況下參與聯合查詢的兩張表都會一大一小,如果是join,在沒有其他過濾條件的情況下mysql會選擇小表作為驅動表,但是left join一般用作大表去join小表,而left join本身的特性決定了mysql會用大表去做驅動表,這樣下來效率就差了不少,如果我把上面那個sql改成
select c.* from hotel_info_original c
join hotel_info_collection h
on c.hotel_type=h.hotel_type and c.hotel_id =h.hotel_id
查詢計畫如下:
很明顯,mysql選擇了小表作為驅動表,再配合(hotel_id,hotel_type)上的索引瞬間降低了好多個數量級。。。。。
另外,我今天還明白了乙個關於left join 的通用法則,即:如果where條件中含有右表的非空條件(除開is null),則left join語句等同於join語句,可直接改寫成join語句。
後記:
隨著檢視mysql reference manual對這個問題進行了更進一步的了解。mysql在執行join時會把join分為system/const/eq_ref/ref/range/index/all等好幾類,連線的效率從前往後
依次遞減,對於我的第乙個sql,連線型別是index,所以幾乎是全表掃瞄的效果。但是我很奇怪我在(hotel_id,hotel_type)兩列上宣告了unique key,根據官方文件連線型別應該是eq_ref才對,
這個問題一直困擾了我兩天,在google和stackoverflow上都沒有找到能夠解釋這個問題的文章,莫非我這個問題無解了?抱著解決這個問題的決心今天又翻看了一遍mysql官方文件
關於優化查詢的部分,看到了這樣一句:這裡的乙個問題是mysql能更高效地在宣告具有相同型別和尺寸的列上使用索引。我感覺我找到了問題所在,於是我將original和 collection表的(hotel_type,hotel_id)的encoding和collation(決定字元比較的規則)全部改成統一的utf8_general_ci,然後再次執行第一條sql的查詢計畫,得到如下結果:
連線型別已經由index優化到了ref,如果將hotel_type申明為not null可以優化到eq_ref,不過這裡影響不大了,優化後這條sql能在0.01ms內執行完。
那麼如何優化left join:
1、條件中盡量能夠過濾一些行將驅動表變得小一點,用小表去驅動大表
2、右表的條件列一定要加上索引(主鍵、唯一索引、字首索引等),最好能夠使type達到range及以上(ref,eq_ref,const,system)
3、無視以上兩點,一般不要用left join~~!
mysql怎麼優化 MYSQL如何優化?
mysql如何優化?結合你的經驗 1.資料庫的設計 盡量把資料庫設計的更小的佔磁碟空間.1 盡可能使用更小的整數型別.mediumint就比int更合適 2 盡可能的定義欄位為not null,除非這個字段需要null.3 如果沒有用到變長字段的話比如varchar,那就採用固定大小的紀錄格式比如c...
MySQL 如何優化
優化總結 列名描述 type 針對單錶的訪問方法,至少要達到range級別,杜絕出現all級別 key實際上使用的索引,如果沒有選擇索引,值是null ref當使用索引列等值查詢時,與索引列進行等值匹配的物件資訊 rows 預估的需要讀取的記錄條數 extra 額外的資訊 比如設計的聯合索引是 na...
如何MySQL優化總結 mysql優化小結
目標 掌握常見的優化方法 一.如何通過慢日誌發現有問題的sql?1 查詢次數多且每次查詢占用時間長的sql 通過pt query digest分析查詢sql 2 io比較大的sql 通過pt query digest分析rows examine項 3 未命中索引的sql 通過pt query dig...