MySQL學習 3 深入淺出索引(上)

2021-09-23 18:42:00 字數 3955 閱讀 5433

innodb的索引模型

參考資料

寫在後面

環境:mysql5.7.24, for linux-glibc2.12 (x86_64)

簡單來說,索引的出現其實就是為了提高資料查詢 的效率,就像書的目錄一樣。對於資料庫的表而言,索引其實就是它的"目錄"。

用於提高讀寫效率的資料結構有很多,以下介紹三種常見、也比較簡單的資料結構:雜湊表、有序陣列、搜尋樹。還有跳表、lsm樹等資料結構也被用於引擎設計中。

雜湊表 是一種鍵-值(key-value)儲存資料的結構。只要輸入待查詢的key,就可以找到其對應的value。

使用者表user:身份證資訊id_card_n、姓名name。

id_card_n1…id_card_n4的值,並不是遞增的,對應的雜湊索引示意圖如下:

上圖中的user2與user4表示的含義為2個使用者的身份證號碼呼叫雜湊函式換算出的value都是n,所以後面跟了乙個鍊錶。

假設此時要查id_card_n2對應的name,處理步驟:首先,將id_card_n2通過雜湊函式算出n;然後,找到n,按順序遍歷找到user2。

設想一下,如果查詢身份證號在[id_card_x,id_card_y]這個區間的使用者,雜湊結構就只能全部掃瞄一遍了。而有序陣列在範圍查詢場景中的優勢就很明顯了。

在使用者表user:身份證資訊id_card_n、姓名name示例中,假設遞增順序如下,用有序陣列實現示意圖:

假設身份證號沒有重複,這個陣列就是按照身份證號遞增的順序儲存的。如果想要查詢id_card_n2對應的name,用二分法就可以快速得到,時間複雜度是o(log(n))。

如果查詢[id_card_x,id_card_y]這個區間的使用者,可以先用二分法找到id_card_x(如果不存在id_card_x,就找到大於id_card_x的第乙個user),然後向右遍歷,直到查到第乙個大於id_card_y的身份證號,退出迴圈。

在使用者表user:身份證資訊id_card_n、姓名name示例用二叉搜尋樹來實現如下:

如果想要查詢id_card_n2對應的name,按照圖中的搜尋順序就是按照 usera->userc->userf->user2這個路徑得到,時間複雜度o(log(n))。

為了維持o(log(n))的查詢複雜度,就需要保持這棵樹是平衡二叉樹。為了做這個保證,更新的時間複雜度也是o(log(n))。

想象一下一棵100萬節點的平衡二叉樹,樹高20。一次查詢可能需要訪問20個資料塊。在機械硬碟時代,從磁碟隨機讀乙個資料塊需要10ms左右的定址時間。也就是說,對於乙個100萬行的表,如果使用二叉樹來儲存,單獨訪問乙個行可能需要20個10ms的時間。

為了讓乙個查詢盡量少地讀磁碟,就必須讓查詢過程訪問盡量少的資料塊。那麼,我們就不應該使用二叉樹,而是要使用n叉樹。n取決於資料塊的大小。

以innodb的乙個整數字段索引為例,這個n差不多是1200。這棵樹高是4的時候,就可以儲存1200的3次方個值(17億+)。考慮到樹根的資料塊總是在記憶體中的,乙個10億行的表上乙個整數字段的索引,查詢乙個值最多隻需要訪問3次磁碟。其實,樹的第二層也有很大概率在記憶體中,那麼訪問磁碟的平均次數就更少了。

假設,表t有乙個主鍵id,表中有字段k,並且在k上有索引。新增5條r1~r5記錄,如下

-- create

mysql>

create

table t(

id int

primary

key,

k int

notnull

, name varchar(16

),index k(k)

)engine

=innodb

;-- insert

mysql>

insert t(

`id`

,`k`

,`name`

)values

(100,1

,'nm1'),

(200,2

,'nm2'),

(300,3

,'nm3'),

(500,5

,'nm5'),

(600,6

,'nm6'

);

表t的主鍵id和k索引的兩棵樹示例如下:

主鍵查詢方式,只需要搜尋id這課b+樹;

mysql>

select

*from t where k=

5;

普通索引查詢方式,則需要先搜尋k索引這課b+樹,得到id的值為500,再到id索引樹搜尋一次。這個過程稱為回表。

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

b+樹為了維護索引有序性,在插入新值的時候需要做必要的維護。

以上圖為例,如果插入新行id=700,則只需要在r5記錄後插入乙個新記錄。

如果插入新行id=400,就相對麻煩了,需要邏輯上挪動後面的資料,空出位置。

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

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

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

自增主鍵是指自增列上定義的主鍵。在建表語句中一般是這麼定義的:not null primary key auto_increment

插入新紀錄時,可以不指定自增列id的值,系統會獲取當前id最大值加1作為下一條記錄的id值。

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

而使用業務邏輯的字段做主鍵,則往往不容易保證有序插入,這樣寫資料成本相對較高。

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

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

使用業務字段 直接做主鍵的場景(典型的kv場景):

索引使用b+樹結構,能夠很好的配合磁碟的讀寫特性,減少單次查詢的磁碟訪問次數。

之前學習了大神丁奇的《mysql實戰45講》,目前在看《高效能高mysql》,也想自己整理一下mysql知識點,發現力不從心,也發現大神之所以是大神,那是因為真的牛。

mysql基礎架構↩︎

深入淺出索引(上)

我們都知道資料庫的索引是用來提公升檢索速度的,但是索引究竟內部是怎麼工作的呢,就來說說這個話題8 索引就像書的目錄一樣,幫助我們更快的查詢到我們需要的資料。索引的種類有很多種,接下來主要介紹三種常見,也比較簡單的資料結構,分別是雜湊表,有序陣列和搜尋樹。然後從使用的角度分析一下,三者的區別 雜湊表是...

04 深入淺出索引(上)

1.索引的作用 提高資料查詢效率,就像書的目錄一樣。2.常見索引模型 雜湊表 有序陣列 搜尋樹 3.雜湊表 鍵 值 key value 4.雜湊思路 把值放在陣列裡,用乙個雜湊函式把key換算成乙個確定的位置,然後把value放在陣列的這個位置 5.雜湊衝突的處理辦法 鍊錶 6.雜湊表適用場景 只有...

03 深入淺出索引上下

三種索引 全文索引,雜湊索引,b 樹索引 索引維護 頁 隨機插入,向右 索引的選擇 身份證id or 自增主鍵 效能 儲存。效能 身份證id,是隨機插入,頁 是中間 可能會造成一定的儲存浪費,次數頁比較多。但是自增主鍵,只有頁滿才會將新點 到下乙個頁,效率是最高的。儲存 乙個b 樹節點,儲存的身份證...