深入淺出MySQL索引(一)

2021-09-27 08:41:07 字數 2794 閱讀 9138

首先,筆者在這裡明確一點:處理資料都是在記憶體中進行的,考慮得都是記憶體中的時間複雜度。

如果我們要操作的資料集非常大,大到記憶體已經沒法處理了怎麼辦?比如資料庫表裡面上千萬條記錄。在這種情況下,對資料的處理需要不斷從硬碟等儲存裝置中調入或者調出記憶體頁面。

一旦涉及到外部儲存裝置,關於時間複雜度的計算就會發生很大變化。試想一下,為了要在乙個擁有幾十萬個檔案的磁碟裡面中查詢乙個文字檔案,讀取上萬次呢,還是讀取幾十次?為了減少對外存裝置的訪問次數,我們就需要新的資料結構來處理這樣的問題。

於是,b樹就由此產生。b樹一種平衡的多路查詢樹,節點中最大的孩子數目成為b樹的階。

在b樹上查詢的過程是乙個順時針查詢結點和在結點中查詢關鍵字的交叉過程。

舉個例子,我們要查詢數字7,首先從外存(比如硬碟中)讀取得到根節點3,5,8三個元素,發現7不在當中,但在5和8之間。因此就通過a2在讀取外存的6,7結點,查詢到所要的元素。

如果記憶體與外存交換資料次數頻繁,會使查詢效率大大降低,那麼b樹怎麼就可以做到減少次數呢?

補充乙個知識點:外存是將所有的資訊分割相等大小的頁面,每次硬碟讀寫的都是乙個或者多個完整的頁面,對於乙個硬碟來說,一頁的長度可能是211或者214個位元組。

在乙個典型的b樹應用(mysql)中,要處理的硬碟資料量很大,因此無法一次全部裝入記憶體。因為我們會對b樹的階數(n)進行調整,使得b樹的階數硬碟儲存的大小相匹配。以innodb的乙個整數字段索引為例,這個n差不多是1200,當這棵樹的高度是3(假設root的高度為1)的時候,就可以存1200的三次方,這已經是17億了。考慮到根節點的資料塊總是在記憶體裡面,乙個10億行的表上整數欄位的索引,查詢乙個值最多隻需要訪問2次硬碟。其實樹的第二層也有很大的概率在記憶體中,那麼訪問硬碟的次數就更少了。

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

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

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

create table t(

id int primary key,

k int not null,

name varchar(16),

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

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

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

主鍵索引的葉子節點存的是整行資料。在 innodb 裡,主鍵索引也被稱為聚簇索引(clustered index)。

非主鍵索引的葉子節點內容是主鍵的值。在 innodb 裡,非主鍵索引也被稱為二級索引(secondary index)。

提乙個問題:基於主鍵的索引和普通索引有什麼區別嗎?

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

b+ 樹為了維護索引有序性,在插入新值的時候需要做必要的維護。以上面這個圖為例,如果插入新的行id值為 700,則只需要在r5的記錄後面插入乙個新記錄。如果新插入的id值為 400,就相對麻煩了,需要邏輯上挪動後面的資料,空出位置。

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

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

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

注意:索引不宜過多,根據業務情況,適當建立索引。實際使用中,最多不要超過6個。

3.3.1 從效能角度

自增主鍵的插入資料模式,符合了遞增插入的場景。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的**。而有業務邏輯的字段做主鍵,則往往不容易保證有序插入,這樣寫資料成本相對較高。

3.3.1 從儲存的空間角度

假設你的表中確實有乙個唯一字段,比如字串型別的統一社會信用**,那應該用統一社會信用**做主鍵,還是自增id呢?

由於每個非主鍵索引的葉子節點上都是主鍵的值。如果用統一社會信用**做主鍵,那麼每個二級索引的葉子節點占用約20個位元組;如果用整型做主鍵,則只要4個位元組;如果是長整型(bigint),需要8個位元組。

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

綜上所述,從效能和儲存空間考慮,自增id往往是最優的原則。

那麼,有什麼場景是業務字段作為主鍵的呢?不妨考慮以下幾點:

有且只有乙個索引

該索引必須是唯一索引

哈哈,細心的讀者一定發現了,這就是典型的kv場景。

b+樹能夠很好地配合磁碟的讀寫特性,減少單次查詢的磁碟訪問次數。由於 innodb 是索引組織表,一般情況建議建立乙個自增主鍵,這樣非主鍵索引占用的空間最小。

深入淺出MySQL索引(二)

先搜尋二級索引 非主鍵索引 找到主鍵,再到主鍵索引樹的過程,稱為回表。在下面這個表t中,如果我執行 select from t where k between 3 and 5,需要執行幾次樹的搜尋操作,會掃瞄多少行?create table t id int primary key,k int no...

深入淺出MySQL

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

深入淺出理解索引

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