MySql優化- join匹配原理(一)


疑問

表:sl_sales_bill_head 訂單抬頭表 數據行:8474

表:sl_sales_bill          訂單明細 數據行:8839

字段:SALES_BILL_NO 訂單號

情況1

沒有任何索引 sql語句

EXPLAIN select * from sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

lh為主表 lb為子表

改一下sql語句

EXPLAIN select * from  sl_sales_bill_head_copy1 lh
 join sl_sales_bill_copy1 lb on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

疑問:為什么sql語句無論主表是哪個 lh都先執行

情況2

sl_sales_bill_head_copy1 的SALES_BILL_NO為主鍵索引

 

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;

 

sql語句1:

EXPLAIN select * from  sl_sales_bill_head_copy1 lh
 join sl_sales_bill_copy1 lb on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

sql語句2:

EXPLAIN select * from   sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

疑問:為什么無論怎么通過sql語句改變主表 始終是lb先執行

情況3 

lh.SALES_BILL_NO創建索引

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;

 

 EXPLAIN select * from   sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 
where lb.SALES_BILL_NO='HP20190410000099'  
 EXPLAIN select * from   sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 
where lh.SALES_BILL_NO='HP20190410000099'  

都會正常走索引 同時也是lh先執行

如果改為lb的其他字段

EXPLAIN select * from   sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 
where lb.id='0001c3fd44454a65a4122b259283f979'  

無索引情況

ID有索引情況

變成了lb先執行

情況4

sl_sales_bill_head_copy1 的SALES_BILL_NO為主鍵索引

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;

SQL語句

EXPLAIN select * from   sl_sales_bill_copy1 lb
  join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

sql語句

EXPLAIN select * from   sl_sales_bill_copy1 lb
  left join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 

疑問:為什么left join沒有走索引了

Join匹配原理

說明

mysql只支持一種算法Nested-Loop Join(嵌套循環鏈接),不像其他商業數據庫可以支持哈希鏈接和合並連接,不過MySQL的Nested-Loop Join(嵌套循環鏈接)

Simple Nested-Loop

圖片來源:InsideMySQ

 R表為驅動表每掃描一行去S表找匹配的數據 這種算法是最耗時的 總掃描次數為驅動表行數*非驅動表行數 

比如R表有200表數據 S表有100條 總掃描次數為200*100 可以看出這種算法效率最低

Index Nested-Loop Join

R表為驅動表每掃描一行 根據匹配條件通過索引去S表找 這種算法需要非驅動表有索引 一般我們on r.sid=s.id 索引時給非驅動表用的

比較高效

Block Nested-Loop Join

 

mysql 5.5對Simple Nested-Loop的優化  先掃描驅動表一定量(根據join_buffer_size來定) 放到join_buffer  然后遍歷非驅動表 非驅動表每次匹配join_buffer里面的數據 減少掃描次數

比如我們的join_buffer最多只能存放r表3條數據  遍歷R表 每遍歷3條將數據放到join_buffer然后 然后再去遍歷一次s表  每s表遍歷一行跟join_buffer里面的數據進行匹配 遍歷完成釋放join_buffer 重復上面操作

在MySQL當中,我們可以通過參數join_buffer_size來設置join buffer的值,然后再進行操作。默認情況下join_buffer_size=256K

解決疑惑

情況1

lh數據條數8274  lb數據條數8721

疑問:為什么驅動表都是lh表

解答:mysqlsql優化器 默認會將小表作為驅動表

好處:

Block Nested-Loop Join算法

比如lh有4條數據 lb數據條數6 join_buffer是只能存放2條數據   

計算規則為(驅動表遍歷次數*驅動表行數)+(非驅動表遍歷次數*非驅動表行數)=總遍歷次數

我們將lb作為驅動表 掃描行數為(1*6)+(3*4)=18  總掃描行數

我們將lh作為驅動表 (1*4)+(2*6)=16 總掃描行數

可以發現小表作為驅動表掃描的行數更低

情況2:

lh數據條數8274  lb數據條數8721

疑問:為什么lh.SALES_BILL_NO為主鍵索引 驅動表始終是lb

解答:mysql優化器還是以小表為原則 如果大表關聯關系有索引而小表沒有則以有索引的表為驅動表

好處:

這里使用的Index Nested-Loop Join算法

如果使用lh驅動表 首先會遍歷8274次  每次去lb去找 因為關聯關系lb.SALES_BILL_NO沒有做索引 所以非驅動表lb也會全表掃描 總掃描次數就變成8274*8721

如果使用lb為驅動表會遍歷lb表每次通過SALES_BILL_NO去非驅動表lh找 因為lh做了索引 所以通過索引掃描一次就可以找到數據 總掃描次數:8274*1

情況3

lh數據條數8274  lb數據條數8721

疑問:為什么就lh.SALES_BILL_NO有主鍵索引 無論搜索條件是lb.SALES_BILL_NO還是lh.SALES_BILL_NO 都是lh先執行

解答:

     因為on lb.SALES_BILL_NO=lh.SALES_BILL_NO  where lb.SALES_BILL_NO='HP20190410000099'

這個時候雖然lb.SALES_BILL_NO沒有索引  但是關聯查找為lb.SALES_BILL_NO=lh.SALES_BILL_NO  and lb.SALES_BILL_NO='HP20190410000099'

正常查找是lb全表掃描得到HP20190410000099然后去lh通過索引得到SALES_BILL_NO=lh.SALES_BILL_NO的數據

如果設置成lb.SALES_BILL_NO=lh.SALES_BILL_NO  and lh.SALES_BILL_NO='HP20190410000099' 得到結果相同  以小表為驅動表原則sql優化器會優化為類似這樣的語句查找

EXPLAIN select * from   sl_sales_bill_copy1 lb
 join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO 
where lb.id='0001c3fd44454a65a4122b259283f979'  

lh.SALES_BILL_NO 有索引 然后lb.id無論有無索引都是 lb為驅動表 因為lb.id已經縮小了數據范圍  小表原則 所以始終是lb為驅動表

情況4

因為left join相當於強制要求了lb為主表 雖然lh.SALES_BILL_NO有索引 但是join索引主要是給非驅動表用的  所以出現以上情況

join優化原則

盡量減少驅動表條數 非驅動表關聯條件建立索引

雖然大部分會經過mysql優化器自動優化,復雜sql最好通過執行計划查看一下 是否有性能瓶頸

注意不要通過left join 影響sql優化器 將大表作為驅動表

記住join 索引只有在非驅動表上面才能體現作用


免責聲明!

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



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