數據庫之索引模塊

索引模塊除了是數據庫最重要的模塊之一,也是面試中最常常被問到的,關於索引模塊常見問題以下:html

  • 爲何要使用索引
  • 什麼樣的信息能成爲索引
  • 索引的數據結構
  • 密集索引和稀疏索引的區別

爲何要使用索引:mysql

數據庫中最小存儲單位一般是塊或者頁,每一個塊裏面都會包含多行數據。而咱們在查詢一些沒有使用索引的數據時,一般都須要進行全表掃描,也就是說須要加載全部的塊,而後逐個遍歷這些塊直到查找出咱們須要查找的數據。可想而知這種查詢方式在數據量比較大的時候效率是比較慢的,因此咱們不少時候都須要避免全表掃描。不過數據庫的設計者早已考慮到這一點因此引入了更高效的查詢機制,即便用索引。索引的靈感來自於字典,咱們都知道字典會記錄一些關鍵信息,例如偏旁部首拼音等,咱們經過這些關鍵信息就能夠快速查找到那個字所在的頁面。而索引也是如此,數據庫可以經過索引記錄的關鍵信息迅速定位目標數據在哪一個位置上,就能夠避免全表掃描的發生。因此使用索引的目的就是爲了讓查詢更高效。面試

什麼樣的信息能成爲索引:算法

主鍵id,惟一的字段,以及頻繁被做爲查詢條件的字段,若同時多個字段頻繁做爲查詢條件時能夠對這幾個字段創建組合索引sql

索引的數據結構:數據庫

一般是B+樹、Hash以及少數數據庫支持的BitMap數據結構


二叉查找樹

接下來簡單的說下索引的數據結構,咱們都知道索引最經常使用的數據結構是B+樹,在介紹什麼是B+樹以前,首先得了解二叉查找樹和B樹,並簡單說明一下爲何沒有采用二叉樹或B樹做爲索引的數據結構。ide

如今咱們已經知道給字段創建索引的目的是爲了幫助咱們快速定位到目標數據所在的位置,若讓咱們本身去設計索引的話,對於快速查找這個需求可能第一時間就會想到二叉查找樹之類的樹形數據結構。因此本小節先介紹二叉查找樹,並一步一步地瞭解爲什麼在衆多的樹形結構中會採用B+樹做爲索引的數據結構。性能

二叉查找樹是一種經常使用的樹形數據結構,二叉查找樹的每一個節點最多隻有左右兩個子節點,分別成爲左子樹和右子樹,一般左子樹的元素小於它的父節點,而右子樹則大於它的父節點。位於最頂端的節點一般稱爲根節點,二叉查找樹的查找算法是二分查找。下圖是一顆平衡二叉樹,所謂平衡二叉樹就是末端左右兩個節點的高度相差不超過1:
數據庫之索引模塊優化

二叉查找樹因爲同一級最多隻能有兩個節點,且對磁盤IO沒有優化,由於每次IO讀取都只能讀兩個節點,因此並不能達到較理想的查詢速度,不能做爲索引的數據結構。


B樹

因爲二叉樹每次只能讀取兩個節點對磁盤IO沒有優化,而且只有左右兩個查找路徑,樹的深度就會隨着日益增長的數據量而遞增,因此這時候就須要尋找一個每一個層級能夠有多個節點的多路樹形結構,而B樹就符合該需求,B樹又稱爲多路平衡查找樹,其大體結構以下圖:
數據庫之索引模塊

同一層有m個節點一般稱爲m階,一棵m階B樹(balanced tree of order m)是一棵平衡的m路搜索樹。它或者是空樹,或者是知足下列性質的樹:

  1. 根節點至少有兩個子節點
  2. 樹中每一個節點最多含有m個子節點(m >= 2)
  3. 除根節點和葉子節點外,其餘每一個節點至少有ceil(m/2)個子節點
  4. 全部的葉子節點都位於同一層
  5. 假設每一個非終端節點中包含有n個關鍵字信息,其中:
    • Ki (i=1...n)爲關鍵字,且關鍵字按順序升序排序 K(i-1) < Ki
    • 關鍵字的個數 n 必須知足:[ceil(m / 2) - 1] <=n <= m - 1,即任意節點的關鍵字個數上限比它的子樹上限少一個,且對於非葉子節點來講任意節點的關鍵字個數比它的指向孩子的指針個數少一個
    • 非葉子節點的指針:P[1], P[2], ..., p[M]; 其中 P[1] 指向關鍵字小於 K[1] 的子樹①,P[M] 指向關鍵大於 K[M - 1] 的子樹②,其餘 P[i] 指向關鍵字屬於 (K[i - 1], K[i]) 的子樹③

①:某節點最左子節點裏關鍵字的值均小於該節點最左關鍵字的值
②:某節點最右子節點裏關鍵字的值均大於該節點裏全部關鍵字的值
③:某節點除左右之外全部子節點裏關鍵字的值大小,均位於離該子節點指針最近的兩個關鍵字的值之間


B+樹

B 樹雖然已經達到能夠用做於索引數據結構的標準,可是還有更好的替代品,那就是B+樹,從名字也能夠看出B+樹至關因而B樹的變體。其定義基本與B樹相同,除了:

  1. 非葉子節點的子樹指針與關鍵字個數相同
  2. 非葉子節點的子樹指針 P[i],指向關鍵字值[K[i], K[i + 1])的子樹
  3. 非葉子節點僅用來作索引,數據都保存在葉子節點中
  4. 全部葉子節點均有一個鏈指針指向下一個葉子節點,葉子節點造成的鏈會按大小排序

B+樹結構圖:
數據庫之索引模塊

B+樹相比於B樹及其餘樹形數據結構來講,更適合用來作存儲索引,緣由以下:

  • B+ 樹的磁盤讀寫代價更低,B+ 樹因爲非葉子節點只會存儲索引,所以B+ 樹的非葉子節點相對於B 樹來講更小,若是把全部同一內部節點的關鍵字存儲在同一盤塊中,那麼該盤塊所能容納的關鍵字數量也越多,一次性讀入內存中的關鍵字也就越多,相對來講IO讀寫次數也就下降了
  • B+ 樹的查詢效率更加穩定,由於具體數據存儲在葉子節點中,因此不管查詢任何數據都須要從根節點走到葉子節點,那麼全部查詢的長度也就相同,這樣每一個數據查詢的效率就幾乎是相同的
  • B+ 樹更有利於對數據庫的掃描,B 樹在提升了磁盤IO的同時並無解決遍歷元素效率低下的問題,而B+ 樹只須要遍歷葉子節點就能夠解決對所有關鍵字信息的掃描,因此對數據庫中頻繁使用的範圍查詢來講B+ 樹更高效

Hash以及BitMap

除了上一小節所介紹的B+ 樹索引結構以外,還有一個經常使用的Hash索引結構。Hash稍微簡單一些,就是對索引的key進行一次hash計算,而後就能夠定位出數據存儲的位置,因此在某些特定場景來講Hash索引要比B+ 樹索引更高效。如圖:
數據庫之索引模塊

既然理論上來講Hash索引要比B+ 樹索引更高效,可是爲何沒有成爲主流索引結構呢,這是由於Hash索引存在如下缺點:

  • 由於hash的特性,因此僅僅能知足 「=」,「IN」,不能使用範圍查詢
  • 沒法被用來避免數據的排序操做
  • 不能利用部分索引鍵查詢,由於在使用組合索引的時候,Hash索引是將組合索引裏的字段合併後再計算的hash值,而不是單獨計算的hash值。因此不使用組合索引裏所有字段去查詢的話,Hash索引就沒法被利用
  • 不能避免表掃描,由於數據量大的時候就會有出現重複Hash較多的狀況,那麼就得拿出全部相同Hash值的數據來比較才能取到具體的數據,因此廣泛來講數據量越大Hash索引的效率就越低
  • 遇到大量Hash值相等的狀況後性能並不必定就會比B+樹索引高

BitMap:

除了B+ 樹及Hash索引外,還有一種索引結構就是BitMap,即位圖索引,可是僅有少許數據庫支持,因此這裏僅作簡略說起。當表中的某個字段只有幾種值的時候,例如存儲性別信息的字段之類的,在這種字段使用BitMap索引就是最佳的選擇。BitMap結構圖以下:
數據庫之索引模塊

可是BitMap有一個很大的缺陷就是鎖的粒度會很是的大,在新增和更新數據時,與該數據在同一個位圖的數據也會被鎖住。


密集索引和稀疏索引的區別

密集索引和稀疏索引的區別:

  • 密集索引文件中的每一個搜索碼值都對應一個索引值
  • 稀疏索引文件只爲索引碼的某些值創建索引項
  • 密集索引和稀疏索引的主要區別就是前者葉子節點保存完整的數據,然後者保存的是指向data的指針

密集索引和稀疏索引的區別圖:
數據庫之索引模塊

密集索引:葉子節點保存的不只僅是鍵值,還保存了位於同一行數據裏其餘列的信息,因爲密集索引決定了表的物理排列順序,而一個表只能有一個物理排列順序,因此一個表只能建立一個密集索引

稀疏索引:葉子節點僅保存了鍵位信息,以及該行數據的地址或主鍵。因此須要經過數據的地址或主鍵才能進一步定位到數據。

咱們來看看具體到MySQL的主流存儲引擎:

  • MyISAM:不論是主鍵索引、惟一索引仍是普通索引都屬於稀疏索引,因此MyISAM只有稀疏索引,沒有密集索引。而且MyISAM中索引與數據是分開存儲的
  • InnoDB:表只會有且只有一個密集索引,其餘索引都是稀疏索引。而且InnoDB中索引與數據是存儲在同一個文件中的
    • 若一個主鍵被定義,該主鍵則做爲密集索引
    • 若沒有主鍵被定義,該表的第一個惟一非空索引則做爲密集索引
    • 若不知足以上條件,InnoDB內部會生成一個隱藏主鍵做爲密集索引,這個隱藏的主鍵是一個6字節的自增列
    • 非主鍵索引存儲相關鍵位和其餘對應的主鍵值,包含兩次查找

InnoDB與MyISAM引擎的檢索流程對比:
數據庫之索引模塊


索引額外問題之聯合索引最左匹配原則的成因

假設咱們對A、B兩個字段創建聯合索引:(A, B),此時該聯合索引的左邊是A而右邊是B,當執行where A = '' and B = '' 時會走這個(A, B)聯合索引,where A = ''也會走(A, B)聯合索引,可是where B = ''則不會走(A, B)聯合索引。這就是所謂的最左匹配原則

在最左匹配原則中,有以下說明:

  1. 最左前綴匹配原則,很是重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
  2. =和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式

咱們來作個實驗,驗證下最左匹配原則。建表sql以下,該表中有一個聯合索引:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `age` int(11) NOT NULL,
  `sex` varchar(20) NOT NULL,
  `address` varchar(100) NOT NULL,
  `cid` int(11) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_name_age` (`name`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;

當where條件存在name字段時,會使用索引查詢:
數據庫之索引模塊
數據庫之索引模塊

當where條件不存在name字段時,則不會使用索引查詢:
數據庫之索引模塊

當where條件存在name字段時,即使是亂序也會使用索引查詢,由於MySQL的執行優化器會自動調整順序以知足使用索引的條件:
數據庫之索引模塊

參考文章:

如今咱們來回答一下最左匹配原則的成因:

MySQL建立聯合索引時,是先對聯合索引中最左字段的數據進行排序,在最左字段排序的基礎上,再對後一個字段的數據進行排序,相似於order by 字段1,order by 字段2 這樣的一種排序規則。因此聯合索引中最左字段是絕對有序的,然後一個字段則是無序的了,所以使用除最左字段之外的字段進行條件查詢是利用不到索引的,這就是最左匹配原則的成因

數據庫之索引模塊


索引額外問題之索引是創建越多越好嗎

答案是否認的,所謂物極必反:

  • 數據量小的表不須要創建索引,創建索引會增長額外的索引維護開銷
  • 數據變動須要維護索引,所以更多的索引意味着更多的維護成本
  • 更多的索引也意味着須要更多的存儲空間
相關文章
相關標籤/搜索