本文中所說的覆蓋索引和索引覆蓋,特指本文中的概念
【1】索引覆蓋
【1.1】索引覆蓋的概念
在我的理解中,什么是索引覆蓋?就是說,你的所有查詢條件中,每個條件CBO都願意去掃描索引來查詢數據(無論是單列索引還是復合索引均可),然后根據索引掃描/查找的一個或多個結果集組合出我們想要查詢的結果集。
然后非聚集索引會根據不同where條件走的索引獲取到葉子節點數據(也就是聚集索引鍵值),這個時候就獲取到了 聚集索引值+本身索引列的值。
最后再拿不同where條件獲取到的聚集索引值做等值比較匹配,均相等的就是我們想要的數據。這個也避免了回表去從實際存儲數據的數據頁去找數據。
【1.2】索引覆蓋實踐(MSSQL索引交叉)
用的是,mssql2014
use master; create table test(id int,num int,num1 int); create clustered index CIX_id on test(id); create index IX_num on test(num); create index IX_num1 on test(num1); --create a test data, 10000 rows ;with temp as ( select 1 as id,5 as num,10 as num1 union all select id+1,num+5,num1+10 from temp where id<10000 ) insert into test select * from temp option(maxrecursion 0);
結果:
如上圖,我們可以看到,並沒有回表,優化器直接分別根據 num 和 num1 去讀取這2列上的索引。
然后獲取到 (1) 聚集索引鍵值+num (2)聚集索引鍵值+num1
最后,直接對比匹配相等的聚集索引值(因為查出來的聚集索引鍵值並不一定保證是有序的《也可以理解成這個時候出來的聚集索引鍵值就是一個堆結果集》,所以這里MSSQL自動用了更優秀的等值匹配算法:HASH MATCH;
【要是不喜歡看圖形界面】可以使用 set showplan_all on/off 開關表格執行計划
問題: 我們前面索引覆蓋概念里說道,【你的所有查詢條件中,每個條件CBO都願意去掃描索引來查詢數據】。
之所以上面可以形成索引覆蓋,是因為2個條件值命中行都很少,所以CBO 都可以根據條件去走索引。
不過,一旦某個條件CBO覺得不值得走索引(比如重復太高、索引行命中太多等等成本代價原因),那么就會變成如普通查詢一樣,能走索引部分走索引,不能走的只能回表查找了。
【1.3】索引覆蓋實踐(MYSQL)
用的是5.7.24 社區版
create database test default character set utf8; use test; create table test1(id int,num int ,num1 int ,num2 int) default character set utf8; create unique index UX_id on test1(id); create index IX_num on test1(num); create index IX_num1 on test1(num1); create index IX_num2 on test1(num2);
--create test data delimiter $$ create procedure sp_insert() begin declare i int; set i=1; while(i<10000) do insert into test1 values(i,i+1,i+2,i+3); set i=i+1; end while; end $$
delimiter ;
call sp_insert();
由於對mysql不是很熟,我們先看看覆蓋索引、正常回表的執行計划,最后再嘗試索引覆蓋。
由上圖我們可以得知
第一行語句是正常的覆蓋索引。
第二行是我們單獨拿num1<10000 查看一下,避免MSSQL和MYSQL的不一致誤導我。事實證明是一致的,果然不走索引。
第三行集合我們的【1.2】中就可以知道,是正常的索引完之后回表查詢。
最后我們試試看,mysql索引覆蓋到底行不行
【mysql索引合並】
參考:https://www.cnblogs.com/gjb724332682/p/11018678.html
這,2個執行計划一摸一樣,就是filtered不一樣,我覺得MYSQL應該沒有本文中指的(索引覆蓋)這么一回事吧(不確定,歡迎大佬指正)。
實際上,經過查找資料,發現mysql 也有索引合並,其原理和上面 【1.2】的 sql server 實驗一樣。
但絕大多數時候是 聚集索引和 二級索引 列作為 where 條件,或者 多是 2個二級索引 在where中 or 的情況下,比如 select num,num1 from test1 where num<10 or num1 <20;
【2】覆蓋索引
【2.1】覆蓋索引的概念
這就是我們經常說的覆蓋索引和索引覆蓋,大多數人並沒有細細區分它們。
那到底什么叫覆蓋索引呢?我的理解是,一個索引包含了所有我們要查的值。
【2.2】覆蓋索引實踐(MSSQL)
use master; create table test1(id int,num int,num1 int,num2 int); create clustered index CIX_id on test1(id); create index IX_num on test1(num); create index IX_num1_2 on test1(num1,num2); --create a test data, 10000 rows ;with temp as ( select 1 as id,5 as num,10 as num1, 11 as num2 union all select id+1,num+5,num1+10,num2+11 from temp where id<10000 ) insert into test1 select * from temp option(maxrecursion 0);
這里以復合索引列為例來演示:
因為復合索引中已經包含了 num2的值,所以直接就可以查詢出來了。
再來一個例子:我們要的結果集加上聚集索引所在列id
同理,因為 id 是聚集索引,已經在(num1,num2) 復合索引的葉子節點里了,所以也滿足覆蓋索引的特性(包含索要查詢的所有數據),這里也沒有回表,直接在索引查詢中就搞定了。
同理單列索引、include包含,這些帶來的效果都是一樣的。
【2.3】覆蓋索引實踐(MYSQL)
這個看【1.3】即可。,mysql是有覆蓋索引的。
【3】總結
索引覆蓋:一個select語句,所有查詢條件中,CBO對於每個條件都願意去掃描索引來查詢數據(無論是單列索引還是復合索引還是其他索引均可),然后根據索引掃描/查找的一個或多個結果集組合出我們想要查詢的結果集。
覆蓋索引:一個select 語句,查詢時,一個索引包含了所有我們要查的值。
個人感覺,索引覆蓋是包含覆蓋索引的,且外界大多理解的覆蓋索引或索引覆蓋等字樣的意思都指的是本文中的覆蓋索引。
本文概念和實驗不一定正確、全面,歡迎大佬拍磚