MySQL覆蓋索引和索引跳躍掃描方式
最近在深入學習MySQL,在學習最左匹配原則的時候,遇到了一個有意思的事情。請聽我細細道來。
我的MySQL版本為8.0.32
可以通過 show variables like 'version'; 查看使用的版本。
準備工作
先建表,SQL語句如下:
create table joint_index_test( id int primary key, a int, b int, c int ); alter table joint_index_test add index index_a_b_c(a,b,c);
表結(jié)構(gòu)非常簡單,4個字段,兩個索引,主鍵索引id和聯(lián)合索引abc。暫時不向表中添加數(shù)據(jù)。
開始測試
接下來我們進行查詢操作和使用explain查看select語句的執(zhí)行
1. 最左匹配原則
explain select * from joint_index_test where a = 3;
這條SQL語句是否走了索引大家基本上都能夠分析出來,基礎比較好的小伙伴甚至可以直接分析出來掃描類型是什么。
執(zhí)行結(jié)果如下圖:
由于where后面的條件是a,遵循聯(lián)合索引的最左匹配原則,會使用索引index_a_b_c,進行查詢。由于我們查詢的列是*,在joint_index_test可以擴展為id,a,b,c,這些列在聯(lián)合索引a,b,c中都可以查詢到。所以MySQL在執(zhí)行的時候,會選擇使用覆蓋索引,不再進行回表查詢?!緀xtra列為Using index】
繼續(xù)進行測試第二條SQL語句
2. 覆蓋索引
explain select * from joint_index_test where b = 3;
根據(jù)最左匹配原則,我們可以判斷出來,第二條SQL語句應該不會使用到index_a_b_c聯(lián)合索引,因為聯(lián)合索引是按照字段的順序從左到右進行構(gòu)建的,也就是從字段a進行從小到大的排序,只有字段a相等的時候才會使用b,c進行排序。也就是說,b、c在全局是無序的,在局部卻是有序的。當我們的條件中缺失聯(lián)合索引最左邊的字段時,MySQL在進行查詢的時候,一般情況下,是不能夠使用到聯(lián)合索引了。
但是也有例外,像上面的這一條SQL語句,執(zhí)行的時候會利用聯(lián)合索引進行全索引掃描,因為我們要查詢的字段在聯(lián)合索引中都可以查詢到,然后將所有查詢到的結(jié)果使用where條件進行篩選。
為什么會優(yōu)先走聯(lián)合索引?
因為二級索引樹的記錄東西很少,就只有「索引列+主鍵值」,而聚簇索引記錄的東西會更多,比如聚簇索引中的葉子節(jié)點則記錄了主鍵值、事務 id、用于事務和 MVCC 的回滾指針以及所有的剩余列。MySQL的查詢是基于成本的,會優(yōu)先原則成本低的查詢方案。
如果我們向joint_index_test表中添加一個name字段,這時候,我們要查詢的所有字段就沒有辦法在聯(lián)合索引中全部找到了,MySQL會放棄聯(lián)合索引,改走全表掃描。
全索引掃描
添加一個name字段后,type從index->ALL
3. 索引跳躍掃描
我們將name字段刪除,表中還只保留 id、a、b、c 四個字段,并向表中生成數(shù)據(jù)。
我們向表中生成一千條數(shù)據(jù),id自增,a對1到6進行枚舉,b、c是int類型的隨機數(shù)。
我們再次執(zhí)行
explain select * from joint_index_test where b = 3;
這條SQL語句,發(fā)現(xiàn)type列和Extra列中的內(nèi)容發(fā)生了變更。
type從index -> range ; Extra列從Using Index -> Using index for skip scan.
之所以發(fā)生了這樣的變化,是MySQL8.0.13后對最左原則失效的情況進行了優(yōu)化。如果我們的聯(lián)合索引構(gòu)建的B+Tree中能夠找到所有查詢的列且where查詢條件沒有遵循最左匹配原則,MySQL會通過索引跳躍掃描進行優(yōu)化處理。提前說明,索引跳躍掃描并不是萬能的,我們在進行SQL查詢的時候還是需要盡可能地遵循最左匹配原則。
接下來,我會根據(jù)MySQL官方文檔對索引跳躍掃描進行解說,感興趣的小伙伴也可以直接點擊文末鏈接,自行閱讀。
在MySQL8.0.13版本之前,執(zhí)行這一條SQL語句,會出現(xiàn) Using where,Using Index 使用索引掃描所有的數(shù)據(jù),之后再利用條件進行過濾,其執(zhí)行type為index對全索引進行掃描,性能僅次于ALL;
從MySQL 8.0.13版本開始,mysql支持多范圍掃描;查詢的條件的每個不同前綴值執(zhí)行子范圍掃描。
例如會對 select * from joint_index_test where b = 3 這條SQL語句通過 distinct a 拆分成六條SQL語句,分別為:
explain select * from joint_index_test where a = 1 and b = 3; explain select * from joint_index_test where a = 2 and b = 3; explain select * from joint_index_test where a = 3 and b = 3; explain select * from joint_index_test where a = 4 and b = 3; explain select * from joint_index_test where a = 5 and b = 3; explain select * from joint_index_test where a = 6 and b = 3;
讓拆分后的語句能夠遵循聯(lián)合索引的最左匹配原則進行范圍查詢,之后對所有查詢到的值進行合并,并作為整體返回。
值得一提的是,索引跳躍掃描,并非跳過索引,而是在缺失的前綴索引的不同值之間進行跳躍;使用這種策略減少了訪問的行數(shù),因為MySQL直接跳過不符合的構(gòu)造范圍的行。
還是那一句話,聯(lián)合索引不是萬能的,之中優(yōu)化是基于以下條件的:
- 只適用于單表查詢;
- 查詢語句中不能使用GROUP BY或DISTINCT;
- 只能對聯(lián)合索引中構(gòu)建的B+數(shù)包含的列進行查詢;
- 缺少的前綴必須是常數(shù),數(shù)字類型的字段
- 查詢條件必須適用連詞進行連接,比如使用AND或者OR
以上還有一些條件,筆者暫時還沒有看懂,值得一提的是,在滿足上面的所有條件的情況下,索引跳躍掃描并不是一定發(fā)生的,因為對缺失的前綴進行組合是需要成本的。
mysql的查詢永遠會選擇成本最低的方案,而索引跳躍掃描僅僅是其中的一種方案。我們可以將索引跳躍掃描看作是覆蓋索引條件查詢?nèi)笔熬Y的一種優(yōu)化方案。
官方鏈接:MySQL :: MySQL 8.0 Reference Manual :: 10.2.1.2 Range Optimization
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL?數(shù)據(jù)類型及最優(yōu)選取規(guī)則
這篇文章主要介紹了MySQL?數(shù)據(jù)類型及最優(yōu)選取規(guī)則,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下,希望對你的學習有所幫助2022-08-08MySQL 5.7.22 二進制包安裝及免安裝版Windows配置方法
這篇文章通過實例代碼給大家介紹了MySQL 5.7.22 二進制包安裝教程,文章末尾給大家補充介紹了mysql 5.7.22 免安裝版Windows配置方法,感興趣的朋友跟隨腳本之家小編一起看看吧2018-08-08Mysql 原生語句中save or update 的寫法匯總
這篇文章主要介紹了Mysql 原生語句中save or update 的寫法匯總,非常詳細,需要的朋友可以參考下2015-03-03MySQL 8.0.13設置日期為0000-00-00 00:00:00時出現(xiàn)的問題解決
這篇文章主要介紹了MySQL 8.0.13設置日期為0000-00-00 00:00:00時出現(xiàn)的問題解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01