索引的各類規則紛繁複雜,不瞭解索引的組織形式就沒辦法真正地理解數據庫索引。經過本文,你能夠深刻地理解數據庫索引在數據庫中到底是如何組織的,今後之後索引的規則對於你將變得清清楚楚、明明白白,不再須要死記硬背。數據庫
順暢地閱讀這篇文章須要瞭解索引、聯合索引、彙集索引分別都是什麼,若是你還不瞭解,能夠經過另外一篇文章來輕鬆理解——數據庫索引是什麼?新華字典來幫你。segmentfault
這篇文章是一系列數據庫索引文章中的第二篇,這個系列包括了下面四篇文章:spa
這一系列涵蓋了數據庫索引從理論到實踐的一系列知識,一站式解決了從理解到融會貫通的全過程,相信每一篇文章均可以給你帶來更深刻的體驗。設計
經過以前的內容,咱們已經對數據庫索引有了至關程度的抽象瞭解,那麼在數據庫中,索引實際是以什麼樣的形式進行組織的呢?同一張表上的多個索引又是怎樣分工合做的呢?3d
目前絕大多數狀況下使用的數據庫索引都是使用B+樹實現的,下面就以MySQL的InnoDB爲例,介紹一下數據庫索引的具體實現。指針
下面是一個以B+樹形式組織的拼音索引,在B+樹中,每個節點裏都有N個按順序排列的值,且每一個值的中間和節點的頭尾都有指向下一級節點的指針。在查找過程當中,按順序從頭至尾遍歷一個節點中的值,當發現要找的目標值剛好在一個指針的前一個值以前、後一個值以後時,就經過這個指針進入下一級節點。當最後到達葉子節點,也就是最下層的節點時,就可以找到本身但願查找的數據記錄了。code
在上圖中若是但願找到險
字,那麼咱們首先經過拼音首字母在根節點上按順序查找到了X
和Y
之間的指針,而後經過這個指針進入了第二級節點···, xia, xian, xiang, ···
。以後在該節點上找到了xian
和xiang
之間的指針,這樣就定位到了第519頁開始的一個目標數據塊,其中就包含了咱們想要找到的險
字。blog
由於拼音索引是彙集索引,因此咱們在葉子節點上直接就找到了咱們想找的數據。索引
下面是一個模擬部首索引的組織形式。咱們由根節點逐級往下查詢,可是在最後的葉子節點上並無找到咱們想找的數據,那麼在使用這個索引時咱們是如何獲得最終的結果的呢?回憶以前字典中「檢字表」的內容,咱們能夠看到,在每一個字邊上都有一個頁碼,這就至關於下面這一個索引中葉子節點上險
字與院
字中間的指針,這個指針會告訴咱們真正的數據在什麼地方。rem
下圖中,咱們把非彙集索引(部首索引)和彙集索引(拼音索引)合在一塊兒就能看出非彙集索引最後到底如何查找到實際數據了。非彙集索引葉子節點上的指針會直接指向彙集索引的葉子節點,由於根據彙集索引的定義,全部數據都是按彙集索引組織存儲的,因此全部實際數據都保存在彙集索引的葉子節點中。而從非彙集索引的葉子節點連接到彙集索引的葉子節點查詢實際數據的過程就叫作——回表。
那麼若是咱們只是想要驗證險
字的偏旁是不是雙耳旁「阝」
呢?這種狀況下,咱們只要在部首索引中阝
下游的葉子節點中找到了險
字就足夠了。這種在索引中就獲取到了SQL語句中須要的全部字段,因此不須要再回表查詢的狀況中,這個索引就被稱爲這個SQL語句的全覆蓋索引。
在實際的數據庫中,非彙集索引的葉子節點上保存的「指針」就是彙集索引中全部字段的值,要獲取一條實際數據,就須要經過這幾個彙集索引字段的值從新在彙集索引上執行一遍查詢操做。若是數據量很少,這個開銷是很是小的;但若是非彙集索引的查詢結果中包含了大量數據,那麼就會致使回表的開銷很是大,甚至超過不走索引的成本。因此全覆蓋索引能夠節約回表的開銷這一點在一些回表開銷很大的狀況下就很是重要了。
上圖是一個聯合索引idx_eg(col_a, col_b)
的結構,若是咱們但願查詢一條知足條件col_a = 64 and col_b = 128
的記錄,那麼咱們能夠一路肯定地往下找到惟一的下級節點最終找到實際數據。這種狀況下,索引上的col_a
和col_b
兩個字段都能被使用。
可是若是咱們將查詢條件改成範圍查詢col_a > 63 and col_b = 128
,那麼咱們就會須要查找全部符合條件col_a > 63
的下級節點指針,最後不得不遍歷很是多的節點及其子節點。這樣的話對於索引來講就得不償失了,因此在這種狀況下,數據庫會選擇直接遍歷全部知足條件col_a > 63
的記錄,而再也不使用索引上剩下的col_b
字段。數據庫會從第一條知足col_a > 63
的記錄開始,橫向遍歷以後的全部記錄,從裏面排除掉全部不知足col_b = 128
的記錄。
這就是範圍條件會終止使用聯合索引上的後續字段的緣由。