程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> [慢查優化]聯表查詢注意誰是驅動表 & 你搞不清楚誰join誰更好時請放手讓mysql自行判定

[慢查優化]聯表查詢注意誰是驅動表 & 你搞不清楚誰join誰更好時請放手讓mysql自行判定

編輯:MySQL綜合教程

   (join/left join/inner join等)時的mysql運算過程;

   不要求每個人一定知道線上(現在或未來)哪張表數據量大,哪張表數據量小;

 mysql客戶端(如SQLyog,如HeidiSQL)放在桌面上,


在實例講解之前,我們先回顧一下聯表查詢的基礎知識。

下面兩個查詢,它們只差了一個order by,效果卻迥然不同。

第一個查詢:

ads, city 

  desc

------  -----------  ------  ------  --------------  -------  -------  --------------------  ------  --------  -------------------------------
     1  SIMPLE       city    ref     ads_id,city_id  city_id  4        const                   2838    100.00  Using temporary; Using filesort
     1  SIMPLE       ads     eq_ref  PRIMARY         PRIMARY  4        city.ads_id       1    100.00  Using where                   

第二個查詢:

city 

ORDER BY  desc

執行計劃裡沒有了using temporary:     id  select_type  table   type    possible_keys   key      key_len  ref                     rows  filtered  Extra                      
------  -----------  ------  ------  --------------  -------  -------  --------------------  ------  --------  ---------------------------
     1  SIMPLE       city    ref     ads_id,city_id  city_id  4        const                   2838    100.00  Using where; Using filesort
     1  SIMPLE       ads    eq_ref  PRIMARY         PRIMARY  4        city.ads_id       1    100.00  Using where                為什麼?   DBA告訴我們:     MySQL 表關聯的算法是 Nest Loop Join,是通過驅動表的結果集作為循環基礎數據,然後一條一條地通過該結果集中的數據作為過濾條件到下一個表中查詢數據,然後合並結果。     以上兩個查詢語句,驅動表都是 city,如上面的執行計劃所示!   對驅動表可以直接排序,對非驅動表(的字段排序)需要對循環查詢的合並結果(臨時表)進行排序 因此,order by ads.id desc 時,就要先 using temporary 了!   wwh999 在 2006年總結說,當進行多表連接查詢時, [驅動表] 的定義為:
1)指定了聯接條件時,滿足查詢條件的記錄行數少的表為[驅動表];
2)行數少的表為[驅動表]   既然“未指定聯接條件時,行數少的表為[驅動表]”了, 而且你也對自己寫出的復雜的 Nested Loop Join 不太有把握(如下面的實例所示), 就別指定誰 left/right join 誰了, 請交給 MySQL優化器 運行時決定吧。 如果您對自己特別有信心,可以像火丁一樣做優化。   de.cel 在2012年總結說,不管是你,還是 MySQL, 優化的目標是盡可能減少JOIN中Nested Loop的循環次數, 以此保證:

  先了解一下 mb 表有 千萬級記錄,mbei 表要少得多。慢查實例如下:
LEFT JOIN mbei ON mb.id=mbei.mb_id u ON mb.uid=u.uid  
夠復雜吧。Nested Loop Join 就是這樣, 以驅動表的結果集作為循環的基礎數據,然後將結果集中的數據作為過濾條件一條條地到下一個表中查詢數據,最後合並結果;此時還有第三個表,則將前兩個表的 Join 結果集作為循環基礎數據,再一次通過循環查詢條件到第三個表中查詢數據,如此反復。 這條語句的執行計劃如下:     id  select_type  table   type    possible_keys   key             key_len  ref                     rows  Extra                                       
------  -----------  ------  ------  --------------  --------------  -------  -------------------  -------  --------------------------------------------
     1  SIMPLE       index   userid          userid          4        (NULL)               Using index; 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!
立竿見影,驅動表立刻變為小表 mbei 了, Using temporary 消失了,影響行數少多了:     id  select_type  table   type    possible_keys   key      key_len  ref                             rows  Extra         
------  -----------  ------  ------  --------------  -------  -------  ----------------------------  ------  --------------
     1  SIMPLE       ALL     mb_id  (NULL)   (NULL)   (NULL)                        
     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不變。干嘛要根據非驅動表的字段排序呢?我們前面說過“對驅動表可以直接排序,對非驅動表(的字段排序)需要對循環查詢的合並結果(臨時表)進行排序!”的。
DESC
也滿足業務場景,做到了rows最小:     id  select_type  table   type    possible_keys   key             key_len  ref                    rows  Extra      
------  -----------  ------  ------  --------------  --------------  -------  -------------------  ------  -----------
     1  SIMPLE       mb      index   userid          PRIMARY         4        (NULL)                  
     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 很開心嗎?




    id  select_type  table   type    possible_keys   key      key_len  ref                             rows  Extra         
------  -----------  ------  ------  --------------  -------  -------  ----------------------------  ------  --------------
     1  SIMPLE       ALL     mb_id  (NULL)   (NULL)   (NULL)                        
     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   最後的總結: 強調再強調:
  •  
    記住,explain 是一種美德!
       
    參考資源: 1)wwh999,2006,進行多表查時的排序問題,其多表查詢時的原理論證! ; 2)de.cel,2012,MySQL中的Join 原理及優化思路 ; 3)火丁,2013,MySQL優化的奇技淫巧之STRAIGHT_JOIN;   贈圖一枚: https://www.aspphp.online/shujuku/UploadFiles_3118/201701/2017012015161110.gif

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved