innodb索引模型
主鍵索引和普通索引
頁**和頁合併
主鍵為什麼建議選擇自增主鍵?
可能你了解mysql索引底層採用資料結構b+樹實現的,在某個欄位中建立索引,會加快查詢效率,但是在面試中是遠遠不夠的,在這裡,先丟擲幾個關於索引的面試題:
這幾個面試題都會在索引這兩集中一一揭曉。
第乙個面試題,我在上篇文章就解答了(
一句話簡單的來說,索引的出現就是為了加快資料庫的查詢效率,就好比書的目錄一樣。
舉個生活中的例子,在你眼前有一本500頁的書籍,如果你想要查詢某個知識點,在沒有目錄的情況下,你可能需要一頁一頁翻書查詢;假如這本資料有目錄功能,你只需在目錄上找到對應知識點的頁數即可,效率大幅度提高。同樣mysql也是,索引就是他的"目錄"。
索引的出現為了加快資料庫的查詢效率,但是索引的實現有多種方式,比如雜湊表、有序陣列、搜尋樹、b+樹。這幾種資料結構比較簡單但又是面試常問的問題,希望重點關注一下。下面
我會去介紹一下這幾個資料結構,他們的優缺點是什麼。
雜湊表是關於鍵值對(key-value)儲存的資料結構,使用起來比較簡單,我們只需要輸入特定的key值就可找到對應的value值。實現起來也比較簡單,通過雜湊函式把key值轉換成乙個固定的值,作為桶位,然後將value值儲存在該桶位下。
但使用雜湊表作為索引資料結構的話,存在乙個問題,多個key經過雜湊函式的換算,會出現同一值的情況。處理這種情況,就是在某個桶位上拉出鍊錶進行實現,結構如下所示:
因此使用雜湊表作為索引儲存結構,比較適用於只有等值查詢的場景。比如memcached 及其他一些 nosql 引擎。
有序陣列和雜湊表相比,有序陣列在等值查詢和區間查詢場景中的效能非常優秀。由於陣列為有序的,因此在區間查詢的時候,定位起始值和末尾值即可,效率非常高。等值查詢和雜湊表類似,通過key值找到對應的value值。
但若使用有序陣列作為索引資料結構,如果要往有序陣列中插入某條記錄,則需要挪動該記錄後面的所有記錄,成本較高。
因此,有序陣列索引只適用於靜態儲存引擎,比如你要儲存的是 2017 年某個城市的所有人口資訊,這類不會再修改的資料。
平衡二叉樹是比較常見也是常用的資料結構,他們的特點如下:
使用平衡二叉樹作為索引資料結構,有幾點不足之處:
考慮到極端情況下,每次插入的資料都比上一次插入的資料大,那麼用平衡二叉樹就會以線性方式進行儲存,時間複雜度為o(n)。資料量很大時,在mysql中一張表儲存百萬條資料是很正常的一件事,這樣會導致樹的深度更深,mysql讀取時消耗大量io。
在 innodb 中,表都是根據主鍵順序以索引的形式存放的,這種儲存方式的表稱為索引組織表。innodb引擎預設使用b+樹作為索引資料結構,所有的資料都儲存在b+樹中的。
每乙個索引在 innodb 裡面對應一棵 b+ 樹。
b+樹特點
在innodb引擎中,索引型別可以分為主鍵索引和普通索引。
假設,我們有乙個主鍵列為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),兩棵樹的示例示意圖如下從圖中不難看出,根據葉子節點的內容,索引型別分為主鍵索引和非主鍵索引。
主鍵索引的葉子節點存的是整行資料。在 innodb 裡,主鍵索引也被稱為聚簇索引(clustered index)。
非主鍵索引的葉子節點內容是主鍵的值。在 innodb 裡,非主鍵索引也被稱為二級索引(secondary index)。
根據上面的索引結構說明,我們來討論乙個問題:基於主鍵索引和普通索引的查詢有什麼區別?
如果語句是 select * from t where id=500 ,即主鍵查詢方式,則只需要搜尋 id 這棵 b+ 樹;
如果語句是 select * from t where k=5 ,即普通索引查詢方式,則需要先搜尋 k 索引樹,得到 id 的值為 500,再到 id 索引樹搜尋一次。這個過程稱為回表。也就是說,基於非主鍵索引的查詢需要多掃瞄一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。
b+樹為了維護索引的有序性,在插入新值的時候會做必要的維護。以上圖為例,現在要插入乙個id值為700的記錄,則只需要在id為600的記錄後面新增即可。但如果新增一條id值為400的記錄,就相對麻煩了,需要邏輯上挪動後面的資料,空出位置。
而更糟的情況是,如果 r5 所在的資料頁已經滿了,根據 b+ 樹的演算法,這時候需要申請乙個新的資料頁,然後挪動部分資料過去。這個過程稱為頁**。在這種情況下,效能自然會受影響。
除了效能外,頁**操作還影響資料頁的利用率。原本放在乙個頁的資料,現在分到兩個頁中,整體空間利用率降低大約 50%。
當然有**就有合併。當相鄰兩個頁由於刪除了資料,利用率很低之後,會將資料頁做合併。合併的過程,可以認為是**過程的逆過程。
如果選用自增主鍵的話,每次新增資料時,都是以追加的形式進行儲存的,這樣就不會涉及挪動資料操作,也不會出現上述所說的頁**問題。
如果說採用業務字段作為主鍵的話,每次新增資料時,都會進行索引維護,保持索引的有序性,造成寫資料成本較高。
除了效能方面有區別外,再來看看儲存方面。假設你的表中確實有乙個唯一字段,比如字串型別的身份證號,那應該用身份證號做主鍵,還是用自增欄位做主鍵呢?
表中如果使用自增主鍵的話,若使用bigint做主鍵,佔8個位元組;如果使用身份證號作為主鍵的話,占用20位元組。
所以,主鍵長度越小,普通索引的葉子節點就越小,普通索引占用的空間也就越小,頁儲存索引越多。
上述從效能和儲存空間進行分析採用自增主鍵和採用業務字段作為主鍵的優缺點,也推斷出採用自增主鍵是比較合理的方案。
深入淺出MySQL索引(一)
首先,筆者在這裡明確一點 處理資料都是在記憶體中進行的,考慮得都是記憶體中的時間複雜度。如果我們要操作的資料集非常大,大到記憶體已經沒法處理了怎麼辦?比如資料庫表裡面上千萬條記錄。在這種情況下,對資料的處理需要不斷從硬碟等儲存裝置中調入或者調出記憶體頁面。一旦涉及到外部儲存裝置,關於時間複雜度的計算...
深入淺出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來獲得伺服器...