MySQL LEFT JOIN 性能優(yōu)化策略
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
?1. 關聯(lián)查詢案例介紹我們現(xiàn)在有一個驅動表customer,它存儲客戶id、姓名以及出生日期,默認情況下id是主鍵,沒有任何索引,對此我們給出DDL語句:
customer有一張關聯(lián)表,c_id記錄著與其關聯(lián)數(shù)據(jù)的id,并用available_balance記錄客戶余額,對應DDL如下,可以看到此時我們沒有添加任何索引:
假設此時數(shù)據(jù)庫大約有2000w的數(shù)據(jù),我們希望查出姓名為if2vbdr1kzk47rdmulrxix48tl2r9finmonxpl25cfrqvv7m0t的用戶的出生日期和可用余額,如果沒有記錄余額則設置為null,對應我們給出這樣一條SQL:
最終查詢結果如下,耗時大約是1s多一些,對于用戶而言超過200ms的延遲都是有感知的,所以針對這個查詢我們需要進行相應的優(yōu)化,對此筆者以市面上常見的面經(jīng)為出發(fā)點,逐步拆解并解決這道問題:
2. 講講join的原理join底層關聯(lián)本質上都是基于驅動表(上面的c表)的結果到被驅動表(上面的cb表)進行循環(huán)掃描定位,這里筆者以MySQL5.7、MySQL 8兩個版本對join連接的幾種類型進行介紹: (1) Simple Nested-Loop Join:這也就是我們上文中兩張關聯(lián)表沒有加索引關聯(lián)查詢,得到所有驅動表c的數(shù)據(jù)后,直接給cb表走全表掃描定位匹配,極端情況下要查詢count(c)*count(cb)次,也就是我們傳說中的時間復雜度為O(n^2): (2) Index Nested-Loop Join:這就是join左右字段都加索引后的查詢,這意味著驅動表的選擇不在于我們自身,而是由MySQL優(yōu)化器決定,當驅動表的結果交給被驅動表時,被驅動表直接通過索引定位到關聯(lián)數(shù)據(jù)并阻塞。 (3) Block Nested-Loop Join:沒有索引列的情況都會選擇該算法而不優(yōu)先考慮Simple Nested-Loop Join,Block Nested-Loop Join相比Simple Nested-Loop Join多了一個中間操作,它會將驅動表查詢結果緩存到join buffer,與被驅動表關聯(lián)時會進行批量內(nèi)存關聯(lián)與合并。 (4) HashJoin:這是8.0.18及其之后的版本對于關聯(lián)查詢的優(yōu)化,其原理是針對驅動表join字段進行哈希運算生成結果集存入內(nèi)存中,然后掃描被驅動表并直接通過哈希運算定位到驅動表是否存在關聯(lián)的值已完成結果合并。當然如果驅動表數(shù)據(jù)量大的話,驅動表部分數(shù)據(jù)還會利用磁盤進行分片,生成臨時文件,然后被驅動表同樣是通過哈希運算定位到磁盤分片編號進行物理磁盤IO獲取關聯(lián)結果。 3. 能不能說說這個LEFT JOIN如何加索引上文提到查詢耗時為1s多,針對索引添加我們優(yōu)先使用explain 來分析一下SQL的查詢過程:
以我們的SQL為例該查詢首先查詢驅動表c,它會基于where條件進行全表掃描獲取數(shù)據(jù),基于查詢結果緩存到hash join buffer再到關聯(lián)表即被驅動表的聚簇索引進行全表掃描匹配結果: 這一點我們也可以從執(zhí)行計劃看出,c表和cb表都走了全表掃描,且關聯(lián)查詢時被驅動表cb用到MySQL 8的hash join關聯(lián),這種關聯(lián)方式本質上就說
針對該執(zhí)行計劃,我們進行逐步的調(diào)優(yōu),針對驅動表c的查詢,因為用到了name字段,所以針對name添加一個索引:
經(jīng)過調(diào)整之后,查詢耗時提升為0.739s,查看執(zhí)行計劃,可以看到針對驅動表的慢查詢已經(jīng)走索引了,現(xiàn)在問題就是出在被驅動表cb還是走全表掃描:
所以我們針對被驅動表cb的c_id增加一個索引:
最終查詢耗時優(yōu)化為0.001s,
4. left join on 左右字段是否都需要加索引?為什么?回答這個問題,我們首先需要了解左外連接的工作機制,它本質上就是基于驅動表(也就是上文的c表)的id與被驅動表cb進行鏈接,如果cb沒有數(shù)據(jù)則結果顯示null: 這也就意味著left join左邊的字段是基于where條件的查詢結果篩選出來的數(shù)據(jù),然后遍歷并與被驅動表cb進行關聯(lián),所以如果left join左邊(也就是我們驅動表c的id)如果不作為查詢條件的情況下,可以不加索引,當然我們本次關聯(lián)的id本身就是主鍵,所以這個問題就沒有討論的必要了。 對于left join的右邊,它是作為被驅動表(也就是我們的cb表)的關聯(lián)查詢條件,從執(zhí)行計劃就可以看出如果沒添加索引,它會基于驅動表c給的關聯(lián)條件id進行全表掃描以找到符合條件的數(shù)據(jù),所以為了提升被驅動表cb的檢索速度,關聯(lián)條件c_id是需要增加索引的。 5. 你覺得針對聯(lián)表查詢還有那些優(yōu)化技巧除了上述優(yōu)化技巧,針對關聯(lián)查詢我們可以從表結構設計以及SQL查詢層面考慮優(yōu)化:
轉自https://juejin.cn/post/7459769651342622771 該文章在 2025/6/4 14:42:41 編輯過 |
關鍵字查詢
相關文章
正在查詢... |