MySQL(三)MySQL索引原理
數據庫索引,是數據庫管理系統(DBMS)中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?-----維基百科對數據庫索引的定義
目錄
索引類型
索引結構
二分查找
二叉查找樹(BST Binary Search Tree)
平衡二叉樹(AVL Tree)
多路平衡查找樹(B Tree)
加強版多路平衡查找樹(B+樹)
總結一下InnoDB中B+樹的優點
不同存儲引擎的索引結構
InnoDB
MyISAM
拿漢語字典的目錄頁(索引)打比方,我們可以按拼音、筆畫、偏旁部首等排序的目錄(索引)快速查找到需要的字。
索引類型
在InnoDB 里面,索引類型有三種,普通索引、唯一索引(主鍵索引是特殊的唯一索引)、全文索引。
普通(Normal)索引:也叫非唯一索引,是最普通的索引,沒有任何的限制。
唯一(Unique)索引:唯一索引要求鍵值不能重復。另外需要注意的是,主鍵索引是一種特殊的唯一索引,它還多了一個限制條件,要求鍵值不能為空。主鍵索引用primay key創建。
全文(Fulltext)索引:針對比較大的數據,比如我們存放的是消息內容,有幾KB的數據的這種情況,如果要解決like查詢效率低的問題,可以創建全文索引。只有文本類型的字段才可以創建全文索引,比如char、varchar、text。
MyISAM和InnoDB都支持全文索引。
索引結構
二分查找
對于提高查詢效率的辦法,最常見的就是二分查找法,這在生活中也有非常多的應用。
每一次,我們都把候選數據縮小了一半。對于已經排序過的數據,采用這種方式效率比較高。
如果考慮以有序數組作為索引的數據結構,對于等值查詢和比較查詢效率確實會比較高,但是在更新數據(刪除,新增)的時候可能需要挪動大量的數據(改變其index),所以只適合用來保存靜態的數據。
為了對于頻繁的插入/刪除也有比較高的效率,就需要采用鏈表來實現。如果是簡單的單鏈表,那么它的查找效率是比較差的。
想要兼具數組和鏈表的優勢,就需要一個新的數據結構----二叉查找樹
二叉查找樹(BST Binary Search Tree)
二叉查找樹就是滿足 : 左子樹所有的節點都小于父節點,右子樹所有的節點都大于父節點的數據結構,將其投影到平面上以后,就是一個有序的線性表。
二叉查找樹既能夠實現快速查找,又能夠實現快速插入。
但是二叉查找樹有一個問題:它的查找耗時是和這棵樹的深度相關的,在最壞的情況下,根節點為最大或者最小的時候會變成斜樹,也就是單鏈表,時間復雜度會退化成O(n)
因為左右子樹深度差太大,導致其查詢效率跟普通單鏈表一樣
所以我們就需要一個更加平衡的二叉樹 --- 平衡二叉樹 Balanced binary search trees
平衡二叉樹(AVL Tree)
平衡二叉樹要求左右子樹深度差絕對值不能超過1。
如果此時我們依次插入1,2,3,4,5,6,其樹型結構會是如下圖所示,不會出現斜樹的情形https://www.cs.usfca.edu/~galles/visualization/AVLtree.html
使用上述鏈接演示一下可以發現,當AVL樹出現斜樹的情形時,就會發生旋轉
在平衡二叉樹中,一個節點是一個大小固定的單位,作為索引應該存儲什么內容?
第一個是索引的鍵值。比如我們在id上創建了一個索引,在用where id=1的條件查詢的時候就會找到索引里面的id的這個鍵值。
第二個是數據的磁盤地址。因為索引的作用就是方便我們快速查找數據的存放地址。
第三個是左右子節點的引用
當我們使用AVL樹存儲索引數據時,訪問一個節點就要跟磁盤之間發生一次IO。
InnoDB操作磁盤的最小的單位是一頁(或者叫一個磁盤塊),大小是16K(16384字節)。那么一個樹的節點大小最大就是16K的大小。
如果我們一個節點只存一個鍵值+數據+引用,例如整型的字段,可能只用了十幾個或者幾十個字節,它遠遠達不到16K的容量,所以訪問一個樹節點,進行一次IO的時候,浪費了大量的空間(明明可以讀取16K的數據)。
其次,當數據量非常大的時候,這個AVL樹的高度也會非常高,導致需要和磁盤進行非常多次的IO,很消耗時間
所以就需要在AVL樹的基礎上再次進行優化
1.讓每個節點存儲更多的數據。
2.節點上的關鍵字的數量越多,我們的指針數也越多,也就是意味著可以有更多的分叉(“路數”)。
多路平衡查找樹(B Tree)
跟AVL樹一樣,B樹在枝節點和葉子節點存儲鍵值、數據地址、節點引用。
它有一個特點:分叉數(路數)永遠比關鍵字數多1。
https://www.cs.usfca.edu/~galles/visualization/BTree.html
如圖就是一個多路平衡查找樹(Degree 節點擁有的子樹= 4)
比如MaxDegree(路數)= 4的時候,我們插入數據1、2、3、4,在插入4的時候,本來應該在第一個磁盤塊,但是如果一個節點有四個關鍵字的時候,意味著有5個指針,子節點會變成5?路,所以這個時候必須進行分裂。把中間的數據2提上去,把1變成左子樹節點,3、4變成2的右子樹節點。如果刪除節點,會有相反的合并的操作
從B樹的特性不難發現,在更新索引的時候也會發生大量的索引結構的調整,這就是為什么建議我們不要在頻繁更新的列上建索引,或者為什么不要更新主鍵。
InnoDB中存在頁的概念,默認大小為16K,文件系統中,也有頁的概念。操作系統和內存打交道,最小的單位是頁Page。文件系統的內存頁通常是4K。為什么操作系統操作的最小單位要是一頁呢?這是因為CPU訪問存儲器時,無論是存取指令還是存取數據,所訪問的存儲單元都趨于聚集在一個較小的連續區域中,這就是所謂的局部性原理
假設一行數據大小是1K,那么一個數據頁可以放16行數據,往表中插入數據時,如果一個頁面已經寫完,會產生一個新的Page頁面。如果一個簇的所有的頁面都被用完,會從當前頁面所在段分配一個新的簇。
如果數據不是連續的,在往已經寫滿的頁中插入數據,會導致Page頁面分裂。
這是因為如果插入的數據的索引(一般為主鍵索引) 不是遞增的,那么在插入過程中,就會由于排序的問題,導致新的數據需要破壞原有的page才可以插入使其滿足查找樹的特性,導致頁分裂,從而可能使得樹的高度增加
這也是為什么我們建議使用遞增的數字來做主鍵
但是注意:葉分裂無法完全避免,因為主鍵可以遞增,但是很多其他的索引無法滿足遞增。
加強版多路平衡查找樹(B+樹)
實際上,MySQL使用的索引還不是B樹,而是使用了B樹的改良版本--B+樹
其存儲結構如下所示:
可以把非葉子節點理解成多層目錄,葉子節點理解成目錄下的具體頁碼
MySQL中的B+Tree有幾個特點:
1、它的關鍵字的數量是跟路數相等的;
2、B+Tree的根節點和枝節點中都不會存儲數據,只有葉子節點才存儲數據。搜索到關鍵字不會直接返回,會到最后一層的葉子節點。比如我們搜索 id=28,雖然在第一層直接命中了,但是全部的數據在葉子節點上面,所以還是要繼續往下搜索,一直到葉
子節點。
舉個例子:假設一條記錄是1K,一個葉子節點(一頁)可以存儲16條記錄。我們計算下非葉子節點可以存儲多少個指針?
假設索引字段是bigint 類型,長度為 8 字節。指針大小在 InnoDB 中設置為6 字節,這樣一共 14 字節。非葉子節點(一頁)可以存儲16384/14=1170個這樣的單元(鍵值+指針),代表有1170個指針。
樹 深 度 為 2 的 時 候 , 有 1170^2 個 葉 子 節 點 , 可 以 存 儲 的 數 據 為1170*1170*16=21902400。
在查找數據時一次IO讀取一頁的數據,也就是說,一張2000萬左右的表,查詢數據最多需要訪問3次磁盤
3.B+Tree的每個葉子節點增加了一個指向相鄰葉子節點的指針
它的最后一個數據會指向下一個葉子節點的第一個數據,形成了一個有序鏈表的結構。如果是范圍查詢,在查到一端的最小值之后,可以直接遍歷這個鏈表的結構,直接獲取到所有的值不需要重新從根節點進行IO查找
4、它是根據左閉右開的區間 [ )來檢索數據。
總結一下InnoDB中B+樹的優點
1)它是B樹的變種,也解決B樹解決的問題:每個節點存儲更多關鍵字和更多路數
2) 掃庫、掃表能力更強
如果我們要對表進行全表掃描,只需要遍歷葉子節點就可以了,不需要遍歷整棵B+樹拿到所有的數據
3)B+樹的磁盤讀寫能力相對于B樹來說更強
根節點和枝節點不保存數據,所以一個節點可以保存更多的關鍵字,一次磁盤加載的關鍵字更多
4)排序能力更強
因為葉子節點上有下一個數據區的指針,數據形成了鏈表
5)效率更加穩定
B+樹永遠是在葉子節點拿到數據,所以IO次數是穩定的
不同存儲引擎的索引結構
每張InnoDB 的表會有兩個文件(.frm和.ibd),MyISAM的表會有三個文件(.frm、.MYD、.MYI)。
?.frm是MySQL里面表結構定義的文件,不管你建表的時候選用任何一個存儲引擎都會生成
InnoDB
在InnoDB 里面,它是以主鍵為索引來組織數據的存儲的,所以索引文件和數據文件是同一個文件,都在.ibd文件里面
在InnoDB的主鍵索引的葉子節點上,它直接存儲了表中的數據。
InnoDB中的主鍵索引又稱為聚集索引,非主鍵都是非聚集索引。
所謂聚集索引就是索引鍵值的邏輯順序跟表數據行的物理存儲順序是一致的。(比如字典的目錄是按拼音排序的,內容也是按拼音排序的,按拼音排序的這種目錄就叫聚集索引)。
對于非主鍵索引(輔助索引)存儲的是輔助索引和主鍵值。如果使用輔助索引查詢,會先根據輔助索引查找到對應的主鍵值,再根據主鍵值在主鍵索引中查詢,最終取得數據。
因為B+樹在實現一個節點存儲多個關鍵字,并保持平衡的過程中會發生分叉和合并的操作,這個時候鍵值的地址會發生變化,所以在輔助索引里面不能存儲鍵值的磁盤地址。
因為InnoDB是以主鍵為索引來組織數據的存儲,如果一張表沒有主鍵怎么辦?
1.如果我們定義了主鍵(PRIMARY KEY),那么InnoDB會選擇主鍵作為聚集索引。
2.如果沒有顯式定義主鍵,則InnoDB會選擇第一個不包含NULL值的唯一索引作為主鍵索引。
3.如果也沒有這樣的唯一索引,則InnoDB會選擇內置6字節長的ROWID作為隱藏的聚集索引,它會隨著行記錄的寫入而遞增
MyISAM
MyISAM有兩個額外的文件: .MYD和.MYI
.MYD文件,D代表Data,是MyISAM的數據文件,存放數據記錄; .MYI文件,I代表Index,是MyISAM的索引文件,存放索引
在MyISAM里面,索引和數據是兩個獨立的文件。
在MyISAM的B+Tree 里面,葉子節點存儲的是數據文件對應的磁盤地址。所以從索引文件.MYI中找到鍵值后,會到數據文件.MYD中獲取相應的數據記錄。
在MyISAM中,主鍵索引和輔助索引的檢索方式都是一樣的,都是先在索引文件里面找到磁盤地址,然后到數據文件里面根據磁盤地址獲取對應數據
總結
以上是生活随笔為你收集整理的MySQL(三)MySQL索引原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL(一)SQL执行流程与MySQ
- 下一篇: JDBC连接失败java.sql.SQL