你真的懂數據庫的索引嗎(上篇)

=========================================================================

前情提要

哈哈題目標題黨了,其實我也是個學習的小白,這是學習記錄,共勉 。
微語:慢下來,你才能走得更快。如今的我已經慢慢理解其中的道理了,好了廢話不多說,直接進入主題。索引,一個高大上的名詞,通俗來說就是目錄,通過它能快速定位到你查找的位置。

索引常用數據結構以及B+樹

常用數據結構

首先了解索引之前需要先了解哈希表、有序數組和搜索樹。

  1. Hash表這種數據結構底層是由數組實現的。通過Hash算法把對應的key值轉化成相應的數組下標,value值則存放在數組的這個位置。Hash算法的實現很複雜,經過它的換算,Hash值一般不會相等,但數據多了之後就難以避免了,這個時候就需要解決Hash衝突。解決Hash衝突有兩種方法,一種是開放尋址法,另外一種就是鏈表法。開放尋址法這裏就不概述了,重點來看看鏈表法,其實就是把相同Hash值對應的value值存放到鏈表中。查找的時候就遍歷鏈表得到數據。
    缺點:經過Hash算法算出來的數據一般都不是遞增有序的,所以在進行範圍區間查詢的時候,需要把每個數據都搜索一遍,這個是比較慢的。
    應用場景:適合等值查詢,NoSQL引擎。

  2. 有序數組,顧名思義就是排序的數組,這種結構在等值查詢和範圍查詢都是十分優秀的。可以利用二分法快速查找到數據,但是數組雖然查找速度很快,但是他有個明顯的缺點,就是插入數據的時候,爲了保持數組有序,需要進行大量的數據搬移,這個效率就很低了。
    應用場景:適合靜態存儲引擎,就是不怎麼更新數據

  3. 搜索樹,二叉搜索樹特點是按照中序遍歷可以得到從小到大的排序,因爲它的左子節點小於父節點,而父節點又小於它的右子節點,二叉搜索樹的搜索速度很快,但是同理,當你插入數據的時候,也需要考慮去維持這顆二叉搜索樹的平衡。不過二叉樹的應用場景很少,其原因就是因爲它的樹高,當數據很多的時候,二叉樹的樹高將會很高。索引不止存在內存中,還存在磁盤中,當樹高很高的時候,就意味着,查詢要去訪問很多的數據塊,這個操作是極其耗時的。爲了少讀磁盤,這個時候就應該使用多叉樹。以InnoDB爲例,這個多是1200,當樹的高度爲4的,差不多已經可以存儲17億的數據,一般來說數根節點都是放在內存中的,所以查找這個17億的數據,只需要訪問三次磁盤即可,這樣就可以大大提高效率。
    多叉樹優點:讀寫性能快,適配磁盤訪問方式,所以被廣泛應用在數據庫引擎中。

數據庫底層的存儲基本上都是基於上述的模型。

說完了數據模型,接下來進入正題。Mysql的索引是在存儲引擎中實現的,所以就說明沒有統一的標準,不同引擎有自己不同的工作方式。

InnoDB的索引模型

在InnoDB中,表以主鍵順序爲索引的形式存放的,InnoDB的數據都是存儲在B+樹中的。每個索引就對應一顆B+樹。
B+樹的特徵:
1.有k個子樹的中間節點包含有k個元素(B樹中是k-1個元素),每個元素不保存數據,只用來索引,所有數據都保存在葉子節點。
2.所有的葉子結點中包含了全部元素的信息,及指向含這些元素記錄的指針,且葉子結點本身依關鍵字的大小自小而大順序鏈接。
3.所有的中間節點元素都同時存在於子節點,在子節點元素中是最大(或最小)元素。
B+樹的優勢:
1.單一節點存儲更多的元素,使得查詢的IO次數更少。
2.所有查詢都要查找到葉子節點,查詢性能穩定。
3.所有葉子節點形成有序鏈表,便於範圍查詢。
在這裏插入圖片描述
B+樹和二叉樹、平衡二叉樹一樣都是經典的數據結構。B+樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉子節點上,由各葉子節點指針進行連接。
如下圖:
在這裏插入圖片描述
示例:以字段a爲索引的建表語句

create table T(
id int primary key,
a int not null,
name varchar(16),
index(a) engine = InnoDB;
)

這裏又引出兩個概念:

  1. 主鍵索引:主鍵索引的葉子節點存的是整行數據。在InnoDB中,主鍵索引也被稱爲聚簇索引
  2. 非主鍵索引:非主鍵索引的葉子節點存放的是主鍵的值,在InnoDB中,非主鍵索引被稱爲二級索引

這裏舉個例子來說明上述兩個概念,比如一條SQL語句,SELECT * FROM T WHERE ID = 1
這種主鍵查詢方式,則只會去搜索ID這顆B+樹。如果是SELECT * FROM T WHERE a = 1;這個非主鍵索引,就會先根據a這個索引找到主鍵的值,再拿着主鍵的值去主鍵的b+樹查詢數據。這種方式成爲回表。

索引的維護

在InnoDB中,B+樹要保持索引的有序性,所以在插入新值的時候要對B+樹進行維護。
維護分爲以下幾種情況:

  1. 索引值最大的時候,直接插入到末尾。
  2. 索引值在中間的時候,這個時候就比較麻煩,需要搬動數據。搬動數據的時候,可能會出現頁數據已經滿的情況,這個時候就需要申請一個新的數據頁,並把數據挪過去。這種就稱爲頁分裂。頁分裂操作會引起性能的降低和數據頁的利用率。有頁分裂的同時就有頁合併,當兩個頁刪除了數據的時候,利用率變得很低,這個時候就會對頁進行合併。

自增主鍵可以在一定程度避免這種數據分頁情況的發生,一般來說採用自增主鍵是合理選擇,長度小普通索引的葉子節點就越小,普通索引佔用的空間也就越小。
在特定的業務場景下,只有一個索引,而且這個索引還是唯一索引,這個時候就適合用業務字段來做主鍵。
PS:在沒有主鍵的表,InnoDB會默認創建一個Rowid做主鍵

索引的優化以及一些原則

覆蓋索引

例如SELECT id FROM T WHERE k BETWEEN 1 AND 3這個語句,因爲是直接查詢ID,所以可以避免回表的過程,提高效率,這個就稱爲覆蓋索引。覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。
先來看看一個建立聯合索引SQL語句

CREATE TABLE 'user'(
'id' int(10) NOT NULL,
'name' varchar(32) NOT NULL,
'age' varchar(32) NOT NULL,
PRIMARY KEY('id'),
KEY 'name_age'('name','age') 
)

基於覆蓋索引,我們可以在高頻請求上來建立聯合索引,這樣就不需要回表查詢整行記錄,可以大大提高性能。不過索引的維護是需要浪費資源的,所以在建立來聯合索引的時候需要考慮的更多。

最左前綴原則

B+樹的索引結構,可以利用最左前綴來定位記錄。最左前綴:顧名思義,就是會InnoDB會根據優先根據最左邊的字段來匹配索引,所以在建立聯合索引的時候,要合理安排所以內字段的順序。第一原則,看能否通過調整字段順序,來少維護一個索引,這樣的順序一般就是優先採用的。
如果有聯合索引(a,b)又有基於a,b的各自查詢,像查詢條件裏面只有b的語句的時候,(a,b)這個聯合索引就無法使用,這個時候就必須同時維護(a,b)(b)這兩個索引。這個時候就要考慮空間的問題,由字段的大小來考慮。

索引下推

在MYSQL5.6之前,InnoDB並不會去看聯合索引(a,b)中b的值,只是把匹配a的值取出來,找到對應的ID,並一個個去回表,因爲沒有匹配b的值,所以查詢到的數據可能比較多,這樣回表的話性能消耗也比較大。MYSQL5.6之後就引入索引下推,這樣先匹配a,b的值,過濾後的數據就比較少,也就能減少回表的次數,可以提高效率。

PS:刪除表的記錄,可能索引還在,還是會佔用很多內存,這個時候可以考慮重建索引
如果大家看的覺得有收穫,可以收藏等待索引下篇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章