說點題外的:
MySQL當中的 “My” 是什么意思?
MySQL的發明者名叫 Michael “Monty” Widenius,MySQL是以他女兒的名字 “My” 來命名的。對這位發明者來說,MySQL數據庫就仿佛是他可愛的女兒。
她的二女兒叫什么呢?二女兒叫Maria,MariaDB名字的來源。
正題:
在從一堆數據中查找指定的數據時,我們常用的數據結構是哈希表和二叉查找樹,表本質上就是一堆數據的集合,所以MySQL數據庫用了哈希表和B+樹來實現索引
B+樹是通過二叉查找樹,再由平衡二叉樹,B樹(又名B-樹)演化而來的,B+樹中的B不是代表二叉(binary),而是代表平衡(balance),因為B+樹是從最早的平衡二叉樹演化而來,但是B+樹不是一個二叉樹。
二叉查找樹
二叉查找樹是帶有特殊屬性的二叉樹,需要滿足以下屬性
- 非葉子節點最多擁有兩個子節點
- 非葉子節值大於左邊子節點、小於右邊子節點(左小右大)
- 沒有值相等重復的節點;

對上圖這個二叉樹進行查找,如查鍵值為5的記錄,先找到根,其值時6,大於5,查找6的左子樹,找到4,4小於5,再找其右子樹,一共找了3次。同理,查找鍵值為8的記錄,用了1次。所有鍵值平均查找次數為(1+2+2+3+3+3)/6=2.3次,假如對這些鍵值進行順序查找,平均查找次數為(1+2+3+4+5+6)/6=3.3(查找順序擺放的數,第一個數肯定是1次,而第2個數是2次,以此類推),顯然二叉查找樹的平均查找速度比順序查找更快
二叉查找樹可以任意的構造,假如二叉查找樹按照如下方式構造

平均查找速度為(1+2+3+4+5+5)/6=3.16次,和順序查找差不多。為了提高二叉查找樹的查詢效率,需要二叉查找數是平衡的,這就引出了平衡二叉樹。
平衡二叉樹:
平衡二叉樹除了滿足上面3個屬性,還要滿足如下1個屬性
- 樹的左右兩邊的層級數相差不會大於1
平衡二叉樹的查找效率確實很快,但維護一顆平衡二叉樹的代價是非常大的,需要1次或多次左旋和右旋來得到插入或更新后樹的平衡性。左旋右旋就是選定一個節點作為目標節點,此目標節點就變成左節點/右節點
舉個栗子:
有一個x節點,對x進行左旋意味着將x變為一個左節點

同理有一個y節點,對y進行右旋意味着將y變為一個右節點


B樹和B+樹
B樹和B-樹是同一種樹,假如用平衡二叉樹實現索引效率已經很高了,查找一個節點所做的IO次數是這個節點所處的樹的高度,因為我們無法把整個索引都加載到內存,並且節點數據在磁盤中不是順序排放的。所以最快情況下,磁盤的IO次數為數的高度。
雖然平衡二叉樹查找效率確實很高,但是頻繁的IO才是阻礙提高性能的瓶頸,怎樣減少IO次數呢?前輩們很聰明的提出了局部性原理,分為時間局部性原理,即加入你查詢id為1的用戶數據,過一段時間你還會查詢id為1的數據,所以會將這部分數據緩存下來。空間局部性原理,當你查詢id為1的用戶數據的時候,你有很大的概率會去查詢id為2,3,4的用戶的數據,所以會一次性的把id為1,2,3,4的數據都讀到內存中去,這個最小的單位就是頁。
借用別人的圖展示下
簡單來說CPU進行運算是電子運動,計算速度很快。而將數據從硬盤讀取到內存中是機械運動,很慢。我們在買硬盤的時候經常問這個硬盤是多少轉(每分鍾轉動的圈數),7200轉,5400轉。所以說轉動的越快加載數據越快,但是和CPU比起來差的還很遠,所以說要減低IO次數。
B樹和B+樹的區別:
B+跟B樹不同B+樹的非葉子節點不保存鍵值對應的數據,這樣使得B+樹每個節點所能保存的鍵值大大增加;
B+樹葉子節點保存了父節點的所有鍵值和鍵值對應的數據,每個葉子節點的關鍵字從小到大鏈接;
B+樹的根節點鍵值數量和其子節點個數相等;
B+的非葉子節點只進行數據索引,不會存實際的鍵值對應的數據,所有數據必須要到葉子節點才能獲取到,所以每次數據查詢的次數都一樣;
B樹

B+樹


在B樹的基礎上每個節點存儲的關鍵字數更多,樹的層級更少所以查詢數據更快,所有關鍵字指針都存在葉子節點,所以每次查找的次數都相同所以查詢速度更穩定。除此之外,B+樹的葉子節點是跟后序節點相連接的,這對范圍查找是非常有用的。
B+樹的非葉子節點是主鍵,主鍵占用的空間越小,每個節點能放的主鍵就能更多,這就是為什么我們的主鍵一般不設置太大的原因。主鍵占用的空間小,能降低樹高,減少IO次數。
聚集索引和聯合索引
在InnoDB存儲引擎中,是以主鍵為索引來組織數據的。在InnoDB存儲引擎中,每張表都有個主鍵,如果再創建表時沒有顯示的定義主鍵,則InnoDB存儲引擎會按如下方式選擇或創建主鍵。
首先判斷表中是否有非空的唯一索引,如果有,則該列即為主鍵
如果不符合上述條件,InnoDB存儲引擎自動創建一個6字節大小的指正作為索引
如果有多個非空唯一索引時,InnoDB存儲引擎將選擇建表時第一個定義的非空唯一索引作為主鍵
假如說有如下數據,用戶id為主鍵(1, jack),(2,mike),(3,tom),(4,apple),(5,angle)則數據是這樣存儲的 如圖一

假如說我們現在對用戶名建索引,用戶名索引是怎么存的呢 如圖二

用戶名索引主鍵存儲的是主鍵,所以當我們運行如下sql語句時
select * from table_name where name ="jack"
過程是這樣的,先在name索引上找到對應的主鍵,在根據對應的主鍵去建表時建立的B+樹上找到對應的記錄,即先在圖2上找,再到圖1上找。
聚集索引:數據行的物理順序與列值(一般是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個聚集索引。圖1用的就是聚集索引
非聚集索引:定義:該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同,一個表中可以擁有多個非聚集索引。圖2用的就是非聚集索引
多個鍵值得B+樹是如下存儲的

可以看到鍵值都是排序的,就上面的例子來說(1,1)(1,2)(2,1)(2,4)(3,1)(3,2),數據按照(a,b)的順序進行了存放。
因此對於查詢select * from table where a = xxx and b = xxx,顯然是可以使用(a,b)這個聯合索引的。對於單個的a列查詢select * from table where a = xxx,也可以使用(a,b)這個索引。但對於b列的查詢select * from table where b = xxx,則不可以使用這顆B+樹索引。可以發現葉子節點上的b值為1,2,1,4,1,2,顯然不是排序的,因此對於b列的查詢使用不到(a,b)的索引
那么為什么mysql索引要用b+樹原因如下:
- B+樹能顯著減少IO次數,提高效率
- B+樹的查詢效率更加穩定,因為數據放在葉子節點
- B+樹能提高范圍查詢的效率,因為葉子節點指向下一個葉子節點
