Mysql使用left join連表查詢時,因連接條件未加索引導致查詢很慢


背景

最近一個后台功能列表,業務人員反饋查詢和導出速度非常慢。

通過定位發現列表查詢和數據導出都是使用的同樣的一個連表查詢SQL。

這個功能剛上線不久,起初查詢和導出速度都是蠻快的,把這個SQL放到測試環境也是挺快的。

對比了一下測試環境和生產環境相關表結構都是一樣的,之后我們把目光放在了數量的問題上面,但是幾張關聯表的數據量也不大,不到1w的數據量為何會這么慢呢。

排查

通過Explain發現,連表查詢中的table c沒有使用到索引且是全表掃描。另外在Extra中特別說明了Using join buffer (Block Nested Loop)。

其中table c中的filtered=100% 表示右表沒有應用索引下推(ICP),因為where條件沒有索引。

另外Using join buffer (Block Nested Loop)是因為右表沒有在join列上建索引導致嵌套循環。

解決

通過對table c中的連接字段content_id和user_no分別加上了索引,

加上索引后的執行計划如下 

總結

需要注意:參與join的表,需要在連接條件上建索引。

知識延伸

MySQL使用嵌套循環算法或其變種來進行表之間的連接。

5.5版本之前,MySQL只支持一種表間關聯方式,也就是嵌套循環(Nested Loop)。如果關聯的表數據量很大,那么join關聯的時間會很長。在5.5版本以后,MySQL引入了BNL算法來優化嵌套循環。

1.嵌套循環連接算法(Nested-Loop Join Algorithm)

一個簡單的嵌套循環連接(NLJ)算法從循環中的第一個表中逐行讀取一行,將每行傳遞給處理連接中下一個表的嵌套循環。 這個過程會重復多次,因為還有剩余的表被連接。

假定要使用以下連接類型執行三個表t1,t2和t3之間的連接:

Table   Join Type
t1      range
t2      ref
t3      ALL

如果使用一個簡單的NLJ算法,連接就像這樣處理:

for(row_1 in table_1){
    for(row_2 in table_2){
        if(row_1,row_2滿足join條件){
            ...

            for(row_n in table_n){
                if(row_1,row_2...row_n都滿足join條件){
                    把row_1,row_2...row_n的join結果加到結果集

                }

            }    

      }       

如圖所示

 

這種算法缺陷也很明顯,隨着join表數量的增加,計算量呈指數上升。如果其中出現了一張數據量很大的表,對整個過程的效率也影響很大。

於是,mysql5.5對這個算法進行了優化,新增了Index Nested-loop Join,Block Nested-loop Join。

2.索引嵌套循環連接算法(Index Nested-loop Join Algorithm)

Index Nested-loop Join是針對有索引的情況,而Block Nested-loop Join是針對沒有命中索引的情況。

 

由於索引的效率要比逐條循環效率高,所以當使用索引聯表時,能大大加快查詢速度,但是索引也不是萬能的,如果你需要取索引以外的字段,那么依舊需要回到表中查出相應的數據。

3.塊嵌套循環連接算法(Block Nested-Loop Join Algorithm)

Block Nested-loop Join 塊嵌套循環(BNL)連接算法使用在外部循環中讀取的行的緩沖來減少必須讀取內部循環中的表的次數。 

舉個簡單的例子:外層循環結果集有1000行數據,使用NLJ算法需要掃描內層表1000次,但如果使用BNL算法,則先取出外層表結果集的100行存放到join buffer, 然后用內層表的每一行數據去和這100行結果集做比較,可以一次性與100行數據進行比較,這樣內層表其實只需要循環1000/100=10次,減少了9/10

 

 

參考文章:

https://blog.csdn.net/itas109/article/details/79152144

http://blog.sina.com.cn/s/blog_a1e9c7910102x1bz.html

https://blog.csdn.net/fatesunlove/article/details/105809280

 

本篇文章如有幫助到您,請給「翎野君」點個贊,感謝您的支持。

首發鏈接:https://www.cnblogs.com/lingyejun/p/16074343.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM