索引是數據庫中一個很重要的概念,那麼什麼是索引呢,通俗的講,索引是存儲引擎用於快速找到記錄的一種數據結構,就如同書的目錄,當要查找某一行記錄時,能夠在索引中快速定位所在的位置信息,而後就可直接獲取目標行的記錄。mysql
既然索引的出現是爲了提升查找效率,那麼確定會存在不一樣的索引結構(模型),不一樣的索引模型確定有其適應的場景,在下面文章中,咱們將重點講解常見的索引模型及其特色。算法
用於提升讀寫效率的數據結構有不少,咱們常見的有哈希表、數組和搜索樹。sql
哈希表是以鍵值對(key-value)存儲的數據結構,根據key查找value,只有Memory引擎支持哈希索引,它根據哈希函數將列值key轉換成實際物理位置,而後將value存放在該數組中。 數據庫
可是,多個key通過能夠計算後可能會出現同一個位置的狀況,這種狀況被稱爲哈希衝突,咱們能夠用鏈地址法來解決,其思路是在有衝突的數組索引位置拉出一個鏈表 編程
哈希索引使用的是散列算法,因此存儲的位置很分散,所以該索引適用於等值查詢的場景中,不適用於範圍查詢。數組
數組索引的思路很簡單,它能夠根據索引的位置快速找到存儲的元素,又由於數組是有序的,因此該索引在範圍查詢中效果較好。 緩存
若是僅僅是查詢,採用有序數組的索引確定是很是合適的,可是當向數組中插入數據時,就須要在插入的位置後的元素都向後移,效率很低,所以有序數組索引僅僅適用於查詢操做。性能優化
樹也是一種數據結構,它綜合數組和鏈表的特色,在保證檢索速度的基礎上,同時也保證了數據的插入、刪除和修改的速度。markdown
咱們以二叉搜索樹爲例,咱們看一下二叉搜索樹結構 數據結構
二叉所搜樹定義了在非葉子結點中,左子結點的值小於結點值,右子結點的值大於等於結點值,這樣的二叉樹稱爲二叉排序樹。加入咱們要查找2,它走的路徑爲7-->3-->1-->2,查詢的時間複雜度爲O(log(N)),可是這個時間複雜度依賴於這棵樹爲平衡二叉樹。
樹是能夠存在多叉的,即多叉樹中的每一個結點存在多個子結點,子結點的數據大小要保障從左到右是遞增的。二叉樹是搜索效率最高的,可是在數據庫中並不適用二叉樹,由於索引不只存在內存中,還要寫到磁盤中,爲了讓一個查詢儘量減小讀磁盤次數,就必須讓查詢訪問儘可能少的數據塊,所以使用最多的是多叉樹。
經過上面的內容,瞭解了多叉樹因爲讀寫性能的優勢,以及適配磁盤的訪問模式,已經被普遍應用在數據庫引擎中了。實際上,InnoDB使用的樹模型是B+樹,數據庫表是根據主鍵的順序以索引的形式存放的,每個索引都對應着一個B+樹,在學習B+樹以前,咱們先了解下B樹,最後對比說明爲何數據庫索引選擇B+樹而不選擇B樹。
B樹是一種多叉自平衡搜索樹,它與普通二叉樹的區別在於容許每一個結點有更多的子結點。它設計思想是將更多相關的數據儘可能集中在一塊兒,以便一次讀取多個數據,減小磁盤操做的次數,它可以最大化的優化大塊數據的讀和寫操做,加快了了存取速度。
B樹有如下特色:
- 關鍵字分佈在整個整棵樹中
- 任何一個關鍵字只會出如今樹中的某一個節點
- 搜索效率等價於二分查找
它的優勢是能夠在內部結點同時存儲鍵和值,所以,將頻繁訪問的數據存放在靠近根結點的地方將會提升熱點數據的查詢效率,這種特性使得B樹在特定數據重複屢次查詢的場景中很是的高校。
B+樹是對B樹的進一步優化,B+樹圖以下
B+樹的特色
在InnoDB中使用的索引是B+樹,那麼爲何B+樹比B樹更適合作數據庫的索引呢
B+樹中的內部結點沒有存放數據,因此其內部結點與B樹相比較也就越小,若是把全部同一內部節點的關鍵字都存放在同一盤塊中,該盤塊容納的關鍵字數量就會越多,查詢數據時讀入的內存的關鍵字機會越多,讀寫IO的次數就會下降
B+樹的非葉子結點並非指向文件內容的結點,僅僅是葉子結點中的關鍵字索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。
B樹在提升了IO性能的同時並無解決元素遍歷的我效率低下的問題,正是爲了解決這個問題,B+樹應用而生。B+樹只須要去遍歷葉子節點就能夠實現整棵樹的遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做或者說效率過低。
在B+樹中,根據葉子結點的內容,能夠將索引類型分爲主鍵索引和非主鍵索引
主鍵索引的葉子結點存放的是所查字段的整行數據,在InnoDB裏,主鍵索引也被稱爲聚簇索引,它的索引和數據是存入同一個.idb文件中的,所以它的索引結構是在同一個樹節點中同時存放索引和數據
非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB 裏,非主鍵索引也被稱爲二級索引。
咱們瞭解了主鍵索引和普通索引以後,那麼它們之間有什麼區別呢?
- 若是語句是 select * from T where ID=XXX,即主鍵查詢方式,則只須要搜索 ID 這棵 B+ 樹;
- 若是語句是 select * from T where k=XXX,即普通索引查詢方式,則須要先搜索 k 索引樹,獲得 ID 值,再到 ID 索引樹搜索一次。這個過程稱爲回表。
也就是說,基於非主鍵索引的查詢須要多掃描一棵索引樹。所以,咱們在應用中應該儘可能使用主鍵查詢。
那麼有沒有可能存在這樣一種狀況,僅僅使用普通索引而不須要回表就能夠拿到所需的數據呢?答案是能夠的,覆蓋索引就能夠知足這樣的要求。
覆蓋索引就是select的數據列只用從索引中就可以取得,沒必要從數據表中讀取,換句話說查詢列要被所使用的索引覆蓋。即普通索引中除了包含指向的ID以外,也能夠存放數據。
由於覆蓋索引能夠減小樹的搜索次數,顯著提高查詢性能,全部覆蓋索引是一個經常使用的性能優化手段。
聯合索引又可稱爲複合索引,對於這種類型的索引,MySQL從左到右的使用索引中的字段,一次查詢只能使用索引中的一部分,而且是作左側部分,知足最左前綴原則。
最左前綴原則能夠這樣理解,例如索引key index(a,b,c),它就支持索引(a),(a,b),(a,b,c)3種類型的組合進行查找,不支持索引b、c查找。
利用符合索引中的附加列,能夠縮小搜索的範圍,但使用一個具備兩列的索引 不一樣於使用兩個單獨的索引。複合索引的結構與電話簿相似,人名由姓和名構成,電話簿首先按姓氏對進行排序,而後按名字對有相同姓氏的人進行排序。若是您知道姓,電話簿將很是有用;若是您知道姓和名,電話簿則更爲有用,但若是您只知道名不姓,電話簿將沒有用處。
惟一索引是索引列中的元素是不可重複的,只能出現一次,它與普通索引有以下的區別
查詢操做
普通索引:查找到知足條件的第一個記錄後,須要查找下一個記錄,直到碰到第一個不知足條件的記錄
惟一索引:因爲索引定義了惟一性,查找到第一個知足條件的記錄後,就會中止繼續檢索
所以,普通索引相對於惟一索引要多一些操做。可是,他們之間對於性能的差距倒是微乎其微
更新操做
普通索引:當須要更新一個數據頁時,若是數據頁在內存中就直接更新,而若是這個數據頁尚未在內存中的話,在不影響數據一致性的前提下,InnoDB會將這些更新操做緩存在change buffer中,這樣就不須要從磁盤中讀入這個數據頁了。在下次查詢須要訪問這個數據頁的時候,將數據頁讀入內存,而後執行change buffer中與這個頁有關的操做。經過這種方式就能保證這個數據邏輯的正確性。
惟一索引:全部的更新操做都要先判斷這個操做是否違反惟一性約束,而這必需要將數據頁讀入內存才能判斷。若是都已經讀入到內存了,那直接更新內存會更快,就不必使用change buffer了。
總結
在查詢操做中,由於倆者區別不大,選擇任何一種都是能夠的。在更新操做中,惟一索引相較於普通索引更加消耗IO資源,因此選擇普通索引。綜合分析來看,選擇普通索引。
簡單來講,索引下推是數據庫檢索數據過程當中爲減小回表次數而作的優化。
案例
有一個聯合索引(name,age),如今的需求是檢索出表中的名字的第一個字是郎,並且年齡是23歲的男生,SQL能夠這樣寫
mysql> select * from tuser where name like '郎%' and age=10 and ismale=1; 複製代碼
MySQL5.6以前,
無索引下推
在最左前綴前提下,只能利用最左邊的索引「郎」,找到第一個知足條件的ID,而後進行回表,到主鍵索引上找出數據行,再對比字段值。
有索引下推
有索引下推能夠在索引遍歷過程當中,對索引中包含的字段先作判斷,直接過濾掉不知足條件的記錄,減小回表次數。即InnoDB 在 (name,age) 索引內部就判斷了 age 是否等於 10,對於不等於 10 的記錄,直接判斷並跳過。在咱們的這個例子中,只須要對 ID四、ID5 這兩條記錄回表取數據判斷,就只須要回表 2 次。
參考文章
[1]林曉斌.《MySQL實戰45講》
[2]https://www.jianshu.com/p/0371c9569736
關注公衆號:10分鐘編程
公衆回覆success領取學習資源