深入淺出MySQL索引(二)

2021-09-27 09:12:14 字數 3524 閱讀 1322

先搜尋二級索引(非主鍵索引),找到主鍵,再到主鍵索引樹的過程,稱為回表。

在下面這個表t中,如果我執行 select * from t where k between 3 and 5,需要執行幾次樹的搜尋操作,會掃瞄多少行?

create table t (

id int primary key,

k int not null default 0,

s varchar(16) not null default '',

index k(k) ) engine=innodb default charset=utf8;

insert into t values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

索引搜尋樹如下:

簡單分析一下,這個sql的執行流程:

在k索引樹上找到k=3的記錄,取得 id = 300;

再到id索引樹查到id=300對應的r3;

在k索引樹取下乙個值k=5,取得id=500;

再回到id索引樹查到id=500對應的r4;

在k索引樹取下乙個值k=6,不滿足條件,迴圈結束。

在這個查詢過程中,讀了k索引樹的3條記錄(步驟1,3,5),回表了兩次(步驟2,4)。

如果執行的語句是select id from t where k between 3 and 5,這時只需要查id的值,而id的值已經在k索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢裡面,索引k已經「覆蓋了」我們的查詢需求,這就是覆蓋索引。

覆蓋索引可以減少樹的搜尋次數,顯著提公升查詢效能,使用覆蓋索引是乙個常用的效能優化手段。

基於上面覆蓋索引的說明,我們來討論乙個問題:在乙個市民資訊表上,是否有必要將身份證號和名字建立聯合索引?

假設這個市民表的定義是這樣的:

create table `tuser` (

`id` int(11) not null,

`id_card` varchar(32) default null,

`name` varchar(32) default null,

`age` int(11) default null,

`ismale` tinyint(1) default null,

primary key (`id`),

key `id_card` (`id_card`),

key `name_age` (`name`,`age`)

) engine=innodb default charset=utf8;

如果現在有乙個高頻請求,要根據市民的身份證號查詢他的姓名,這個聯合索引就有意義了。它可以在這個高頻請求上用到覆蓋索引,不再需要回表查整行記錄,減少語句的執行時間。

當然,索引欄位的維護總是有代價的。因此,在建立冗餘索引來支援覆蓋索引時就需要權衡考慮了。

b+樹可以利用最左字首原則,來定位記錄。

下面,我們用(name,age)這個聯合索引來分析。

當你的邏輯需求是查到所有名字是「張三」的人時,可以快速定位到id4,然後向後遍歷得到所有需要的結果。

如果你要查的是所有名字第乙個字是「張」的人,你的sql語句的條件是"where name like 『張%』"。這時,你也能夠用上這個索引,查詢到第乙個符合條件的記錄是id3,然後向後遍歷,直到不滿足條件為止。

這個最左字首可以是聯合索引的最左n個字段,也可以是字串索引的最左m個字元

那麼,如果既有聯合查詢,又有基於a、b各自的查詢呢?查詢條件裡面只有b的語句,是無法使用(a,b)這個聯合索引的,這時候你不得不維護另外乙個索引,也就是說你需要同時維護(a,b)、(b) 這兩個索引。

這時候,我們要考慮的原則就是空間了。比如上面這個市民表的情況,name欄位是比age欄位大的 ,那我就建議你建立乙個(name,age)的聯合索引和乙個(age)的單字段索引。

我們還是以市民表的聯合索引(name, age)為例。如果現在有乙個需求:檢索出表中「名字第乙個字是張,而且年齡是10歲的所有男孩」。那麼,sql語句是這麼寫的:

select * from tuser where name like '張%' and age=10 and ismale=1;
這個語句在搜尋索引樹的時候,只能用 「張」,找到第乙個滿足條件的記錄id3。然後判斷其他條件是否滿足。

在mysql 5.6之前,只能從id3開始乙個個回表。到主鍵索引上找出資料行,再對比字段值。

mysql 5.6 引入的索引下推優化(index condition pushdown), 可以在索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。

無索引下推的執行流程

有索引下推的執行流程

在無索引下推的執行流程裡面,在(name,age)索引,這個過程innodb並不會去看age的值,只是按順序把「name第乙個字是』張』」的記錄一條條取出來回表。因此,需要回表4次。

在有索引下推的執行流程裡面,innodb在(name,age)索引內部就判斷了age是否等於10,對於不等於10的記錄,直接判斷並跳過。在這個例子中,只需要對id4、id5這兩條記錄回表取資料判斷,就只需要回表2次。

判斷下面的sql,找到沒有用到的索引。

create table `geek` (

`a` int(11) not null,

`b` int(11) not null,

`c` int(11) not null,

`d` int(11) not null,

primary key (`a`,`b`),

key `c` (`c`),

key `ca` (`c`,`a`),

key `cb` (`c`,`b`)

) engine=innodb default charset=utf8;

select * from geek where c=n order by a limit 1;

select * from geek where c=n order by b limit 1;

丁奇 《mysql實戰45講》

深入淺出MySQL索引(一)

首先,筆者在這裡明確一點 處理資料都是在記憶體中進行的,考慮得都是記憶體中的時間複雜度。如果我們要操作的資料集非常大,大到記憶體已經沒法處理了怎麼辦?比如資料庫表裡面上千萬條記錄。在這種情況下,對資料的處理需要不斷從硬碟等儲存裝置中調入或者調出記憶體頁面。一旦涉及到外部儲存裝置,關於時間複雜度的計算...

深入淺出MySQL

說明索引的設計和使用 sql中的安全問題 常用sql技巧 sql優化過程 優化資料庫物件 鎖問題 本站 深入淺出mysql 個人部落格 深入淺出mysql 1 設計索引原則 2 小常識 3 btree索引 1 了解sql執行頻率 使用show session global statusa來獲得伺服器...

深入淺出理解索引

一 深入淺出理解索引結構 實際上,您可以把索引理解為一種特殊的目錄。sql server提供了兩種索引 聚集索引 clustered index,也稱聚類索引 簇集索引 和非聚集索引 nonclustered index,也稱非聚類索引 非簇集索引 下面,我們舉例來說明一下聚集索引和非聚集索引的區別...