MySQL InnoDB 的索引模型(B 樹)

2021-10-03 07:31:35 字數 3613 閱讀 4853

目錄一、innodb 的索引模型

二、索引維護

三、mysql-innodb索引二次查詢解決方案

3.1 為什麼會造成二級查詢

3.2 解決方案

3.2.1 索引覆蓋

3.2.2 延時關聯

四、小結

在 mysql 中,索引是在儲存引擎層實現的,所以並沒有統一的索引標準,即不同儲存引擎的索引的工作方式並不一樣。而即使多個儲存引擎支援同一種型別的索引,其底層的實現也可能不同。由於 innodb 儲存引擎在 mysql 資料庫中使用最為廣泛,所以下面我就以 innodb 為例,和你分析一下其中的索引模型。

在 innodb 中,表都是根據主鍵順序以索引的形式存放的,這種儲存方式的表稱為索引組織表。又因為前面我們提到的,innodb 使用了b+索引模型,所以資料都是儲存在 b+ 樹中的。

每乙個索引在 innodb 裡面對應一棵 b+ 樹。

假設,我們有乙個主鍵列為 id 的表,表中有字段 k,並且在 k 上有索引。

這個表的建表語句是:

mysql> create table t(

id int primary key,

k int not null,

name varchar(16),

index (k))engine=innodb;

表中 r1~r5 的 (id,k) 值分別為 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),兩棵樹的示例示意圖如下。

從圖中不難看出,根據葉子節點的內容,索引型別分為主鍵索引非主鍵索引

根據上面的索引結構說明,我們來討論乙個問題:基於主鍵索引和普通索引的查詢有什麼區別?

也就是說,基於非主鍵索引的查詢需要多掃瞄一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。

b+ 樹為了維護索引有序性,在插入新值的時候需要做必要的維護。以上面這個圖為例,如果插入新的行

id 值為

700,則只需要在

r5 的記錄後面插入乙個新記錄。如果新插入的

id 值為

400,就相對麻煩了,需要邏輯上挪動後面的資料,空出位置。

而更糟的情況是,如果 r5 所在的資料頁已經滿了(即乙個資料頁中儲存的索引資料超過了規定的度數),根據 b+ 樹的演算法,這時候需要申請乙個新的資料頁,然後挪動部分資料過去。這個過程稱為頁**。在這種情況下,效能自然會受影響。

除了效能外,頁**操作還影響資料頁的利用率。原本放在乙個頁的資料,現在分到兩個頁中,整體空間利用率降低大約 50%。

當然有**就有合併。當相鄰兩個頁由於刪除了資料,利用率很低之後,會將資料頁做頁合併。合併的過程,可以認為是**過程的逆過程

基於上面的索引維護過程說明,我們來討論乙個案例:

你可能在一些建表規範裡面見到過類似的描述,要求建表語句裡一定要有自增主鍵。當然事無絕對,我們來分析一下哪些場景下應該使用自增主鍵,而哪些場景下不應該。

自增主鍵是指自增列上定義的主鍵,在建表語句中一般是這麼定義的:

not null primary key auto_increment
插入新記錄的時候可以不指定 id 的值,系統會獲取當前 id 最大值加 1 作為下一條記錄的 id 值。

也就是說,自增主鍵的插入資料模式,正符合了我們前面提到的遞增插入的場景。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的**。

而有業務邏輯的字段做主鍵,則往往不容易保證有序插入,這樣寫資料成本相對較高(也就是上面涉及到的插入資料時出現的效能損耗)。

除了考慮效能外,我們還可以從儲存空間的角度來看。假設你的表中確實有乙個唯一字段,比如字串型別的身份證號,那應該用身份證號做主鍵,還是用自增欄位做主鍵呢?

由於每個非主鍵索引的葉子節點上都是主鍵的值。如果用身份證號做主鍵,那麼每個二級索引的葉子節點占用約 20 個位元組,而如果用整型做主鍵,則只要 4 個位元組,如果是長整型(bigint)則是 8 個位元組。

顯然,主鍵長度越小,普通索引的葉子節點就越小,普通索引占用的空間也就越小

所以,從效能和儲存空間方面考量,自增主鍵往往是更合理的選擇

有沒有什麼場景適合用業務字段直接做主鍵的呢?還是有的。比如,有些業務的場景需求是這樣的:

只有乙個索引;

該索引必須是唯一索引。

你一定看出來了,這就是典型的 kv 場景。

由於沒有其他索引,所以也就不用考慮其他索引的葉子節點大小的問題。

這時候我們就要優先考慮上一段提到的「盡量使用主鍵查詢」原則,直接將這個索引設定為主鍵,可以避免每次查詢需要搜尋兩棵樹。

因為innodb二級索引儲存的是主鍵,所以通過索引查詢時,第一次查詢是通過二級索引找到主鍵值,第二次查詢是通過主鍵在聚簇索引找到對應的行位置

乙個包含查詢所需的字段的索引稱為 

covering

index

覆蓋索引。

mysql

只需要通過索引就可以返回查詢所需要的資料,而不必在查到索引之後進行回表操作,減少

io,提供效率。

select index_column from table 1
只要讓二級索引包含了要查詢的字段,則不需要再去聚簇索引進行二次查詢

延遲關聯:通過使用

覆蓋索引

查詢返回需要的主鍵,

再根據主鍵

關聯原表獲得需要的資料。

select * from orders o 

inner join

(select order_id from orders where addtime = '') o1

on o.order_id = o1.order_id;

在優化分頁查詢時用到了延時關聯提高查詢效率

select * from orders o1 

inner join

(select order_id from orders order by addtime limit 1902,20) o2

on o1.order_id = o2.order_id

今天,我跟你分析了資料庫引擎可用的資料結構,介紹了 innodb 採用的 b+ 樹結構,以及為什麼 innodb 要這麼選擇。b+ 樹能夠很好地配合磁碟的讀寫特性,減少單次查詢的磁碟訪問次數。盡量將b+樹的資料頁大小設定的和磁碟的頁大小一致,可以有效減少io次數(季計算機儲存器是通過頁來管理的,作業系統中學的)

由於 innodb 是索引組織表,一般情況下我會建議你建立乙個自增主鍵,這樣非主鍵索引占用的空間最小。但事無絕對,我也跟你討論了使用業務邏輯字段做主鍵的應用場景。

關於 Mysql innodb的索引

關於innodb的索引,可以分為聚簇索引,輔助索引,都是以b tree 為底層資料結構。聚簇索引 只是資料的儲存方法。以主鍵為key,如果表中沒有主鍵,則會選擇乙個有唯一索引的列作為key,如果都沒有,innodb會為我們建立乙個唯一列作為key。所有的資料都存在葉子節點上,並且是按順序儲存的。如果...

MySQL Innodb 索引的原理

回想四年前,我在學習mysql的索引這塊的時候,老師在講索引的時候,是像下面這麼說的 索引就像一本書的目錄。而當使用者通過索引查詢資料時,就好比使用者通過目錄查詢某章節的某個知識點。這樣就幫助使用者有效地提高了查詢速度。所以,使用索引可以有效地提高資料庫系統的整體效能。嗯,這麼說其實也對。但是呢,大...

MySQL Innodb 索引的原理

回想四年前,我在學習mysql的索引這塊的時候,老師在講索引的時候,是像下面這麼說的 索引就像一本書的目錄。而當使用者通過索引查詢資料時,就好比使用者通過目錄查詢某章節的某個知識點。這樣就幫助使用者有效地提高了查詢速度。所以,使用索引可以有效地提高資料庫系統的整體效能。嗯,這麼說其實也對。但是呢,大...