索引的本質
mysql官方對索引的定義為:索引(index)是幫助mysql高效獲取資料的資料結構。提取句子主幹,就可以得到索引的本質:索引是資料結構。
我們知道,資料庫查詢是資料庫的最主要功能之一。我們都希望查詢資料的速度能盡可能的快,因此資料庫系統的設計者會從查詢演算法的角度進行優化。最基本的查詢演算法當然是順序查詢(linear search),這種複雜度為o(n)的演算法在資料量很大時顯然是糟糕的,好在電腦科學的發展提供了很多更優秀的查詢演算法,例如二分查詢(binary search)、二叉樹查詢(binary tree search)等。如果稍微分析一下會發現,每種查詢演算法都只能應用於特定的資料結構之上,例如二分查詢要求被檢索資料有序,而二叉樹查詢只能應用於二叉查詢樹上,但是資料本身的組織結構不可能完全滿足各種資料結構(例如,理論上不可能同時將兩列都按順序進行組織),所以,在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,這樣就可以在這些資料結構上實現高階查詢演算法。這種資料結構,就是索引。
看乙個例子:
上圖展示了一種可能的索引方式。左邊是資料表,一共有兩列七條記錄,最左邊的是資料記錄的實體地址(注意邏輯上相鄰的記錄在磁碟上也並不是一定物理相鄰的)。為了加快col2的查詢,可以維護乙個右邊所示的二叉查詢樹,每個節點分別包含索引鍵值和乙個指向對應資料記錄實體地址的指標,這樣就可以運用二叉查詢在\(o(log_2^n)\)的複雜度內獲取到相應資料。
雖然這是乙個貨真價實的索引,但是實際的資料庫系統幾乎沒有使用二叉查詢樹或其進化品種紅黑樹(red-black tree)實現的,原因會在下文介紹。
二叉排序樹
在介紹b樹之前,先來看另一棵神奇的樹——二叉排序樹(binary sort tree),首先它是一棵樹,「二叉」這個描述已經很明顯了,就是樹上的一根樹枝開兩個叉,於是遞迴下來就是二叉樹了(下圖所示),而這棵樹上的節點是已經排好序的,具體的排序規則如下:
若左子樹不空,則左子樹上所有節點的值均小於它的根節點的值
若右子樹不空,則右字數上所有節點的值均大於它的根節點的值
它的左、右子樹也分別為二叉排序數(遞迴定義)
從圖中可以看出,二叉排序樹組織資料時,用於查詢是比較方便的,因為每次經過一次節點時,最多可以減少一半的可能,不過極端情況會出現所有節點都位於同一側,直觀上看就是一條直線,那麼這種查詢的效率就比較低了,因此需要對二叉樹左右子樹的高度進行平衡化處理,於是就有了平衡二叉樹(balenced binary tree)。
所謂「平衡」,說的是這棵樹的各個分支的高度是均勻的,它的左子樹和右子樹的高度之差絕對值小於1,這樣就不會出現一條支路特別長的情況。於是,在這樣的平衡樹中進行查詢時,總共比較節點的次數不超過樹的高度,這就確保了查詢的效率(時間複雜度為o(logn))
b樹還是直接看圖比較清楚,圖中所示,b樹事實上是一種平衡的多叉查詢樹,也就是說最多可以開m個叉(m>=2),我們稱之為m階b樹,為了體現本部落格的良心之處,不同於其他地方都能看到2階b樹,這裡特意畫了一棵5階b樹 。
總的來說,m階b樹滿足以下條件:
每個節點至多可以擁有m棵子樹。
根節點,只有至少有2個節點(要麼極端情況,就是一棵樹就乙個根節點,單細胞生物,即是根,也是葉,也是樹)。
非根非葉的節點至少有的ceil(m/2)個子樹(ceil表示向上取整,圖中5階b樹,每個節點至少有3個子樹,也就是至少有3個叉)。
非葉節點中的資訊包括[n,a0,k1,a1,k2,a2,…,kn,an],,其中n表示該節點中儲存的關鍵字個數,k為關鍵字且ki
從根到葉子的每一條路徑都有相同的長度,也就是說,葉子節在相同的層,並且這些節點不帶資訊,實際上這些節點就表示找不到指定的值,也就是指向這些節點的指標為空。
b樹的查詢過程和二叉排序樹比較類似,從根節點依次比較每個結點,因為每個節點中的關鍵字和左右子樹都是有序的,所以只要比較節點中的關鍵字,或者沿著指標就能很快地找到指定的關鍵字,如果查詢失敗,則會返回葉子節點,即空指標。
例如查詢圖中字母表中的k:
從根節點p開始,k的位置在p之前,進入左側指標。
左子樹中,依次比較c、f、j、m,發現k在j和m之間。
沿著j和m之間的指標,繼續訪問子樹,並依次進行比較,發現第乙個關鍵字k即為指定查詢的值。
b樹搜尋的簡單偽演算法如下:
btree_search(node, key) =floor(pagesize/(keysize+datasize+pointsize))
floor表示向下取整。由於b+tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的效能。
mysql索引實現
在mysql中,索引屬於儲存引擎級別的概念,不同儲存引擎對索引的實現方式是不同的,本文主要討論myisam和innodb兩個儲存引擎的索引實現方式。
myisam索引實現
這裡設表一共有三列,假設我們以col1為主鍵,則上圖是乙個myisam表的主索引(primary key)示意。可以看出myisam的索引檔案僅僅儲存資料記錄的位址。在myisam中,主索引和輔助索引(secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在col2上建立乙個輔助索引,則此索引的結構如下圖所示:
同樣也是一棵b+樹,data域儲存資料記錄的位址。因此,myisam中索引檢索的演算法為首先按照b+tree搜尋演算法搜尋索引,如果指定的key存在,則取出其data域的值,然後以data域的值為位址,讀取相應資料記錄。
myisam的索引方式也叫做「非聚集」的,之所以這麼稱呼是為了與innodb的聚集索引區分。
innodb索引實現
雖然innodb也使用b+tree作為索引結構,但具體實現方式卻與myisam截然不同。
第乙個重大區別是innodb的資料檔案本身就是索引檔案。從上文知道,myisam索引檔案和資料檔案是分離的,索引檔案僅儲存資料記錄的位址。而在innodb中,表資料檔案本身就是按b+tree組織的乙個索引結構,這棵樹的葉節點data域儲存了完整的資料記錄。這個索引的key是資料表的主鍵,因此innodb表資料檔案本身就是主索引。
上圖是innodb主索引(同時也是資料檔案)的示意圖,可以看到葉節點包含了完整的資料記錄。這種索引叫做聚集索引。因為innodb的資料檔案本身要按主鍵聚集,所以innodb要求表必須有主鍵(myisam可以沒有),如果沒有顯式指定,則mysql系統會自動選擇乙個可以唯一標識資料記錄的列作為主鍵,如果不存在這種列,則mysql自動為innodb表生成乙個隱含字段作為主鍵,這個字段長度為6個位元組,型別為長整型。
第二個與myisam索引的不同是innodb的輔助索引data域儲存相應記錄主鍵的值而不是位址。換句話說,innodb的所有輔助索引都引用主鍵作為data域。例如,上圖為定義在col3上的乙個輔助索引:
這裡以英文本元的ascii碼作為比較準則。聚集索引這種實現方式使得按主鍵的搜尋十分高效,但是輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。
了解不同儲存引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了innodb的索引實現後,就很容易明白為什麼不建議使用過長的字段作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作為主鍵在innodb中不是個好主意,因為innodb資料檔案本身是一棵b+tree,非單調的主鍵會造成在插入新記錄時資料檔案為了維持b+tree的特性而頻繁的**調整,十分低效,而使用自增字段作為主鍵則是乙個很好的選擇。
mysql底層 索引
mysql只是乙個應用軟體,不能直接讀取磁碟上的資料,當我們需要讀取某條資料的時候,mysql呼叫核心的乙個函式,告訴核心我要讀取某個資料,核心驅動磁柱磁頭去讀取資料,讀取資料之後怎麼返回裡,其實是把資料寫到記憶體中了 這個記憶體只是核心記憶體,並不是mysql記憶體 接下來再把資料copy到mys...
mysql索引底層
資料結構 二叉樹 從父節點開始,大的往右,小的往左,當有序的增長就變成了單邊增長,就會對效能沒什麼提公升,不適合 紅黑樹 二叉樹的平衡版,通過自旋等方式實現平衡 當資料很多時,紅黑樹的高度就變得很高,查詢一次需要多次的io不適合大資料查詢 b樹和b 樹 乙個節點都可以存多個元素 多叉樹 以有序的方式...
mysql 索引 層數 mysql 索引底層
hash索引o 1 b 樹索引 o logn 為什麼紅黑樹出現了,因為防止某些情況下二叉排序樹退化為鍊錶 誕生了二叉排序平衡樹 樹的效能取決於樹的高度 為什麼db要用m路b樹,為了再降低樹的高度,減少db 磁碟io 次數,如果在記憶體中,紅黑樹效率更高 為什麼m不能無限大,因為會退化成有序陣列,無法...