1. 程式人生 > 實用技巧 >索引的底層資料結構

索引的底層資料結構

Q1:大家使用索引有沒有想過這個問題?為什麼索引能夠幫助mysql高效獲取資料?我一一給大家道來!在給大家講之前,先更大家分享一些計算機基礎知識,有助於理解

A:1:MySQL的資料都是以檔案的形勢儲存在磁碟上的。

2:磁碟是由一圈一圈的磁軌組成。

3:磁頭移動到不同的磁軌,磁碟旋轉,這樣就可以讀到資料。

  4:磁碟存取原理 1.尋道時間(速度慢,費時)  2.旋轉時間(速度較快)

  5:CPU讀取資料都是從記憶體讀取,記憶體去磁碟中讀取資料,記憶體讀取磁碟資料大小都是以頁的大小單位。一頁 = 10kb

  

總結:1:當磁頭移動到另一個磁軌讀取資料就是我們常說的一次I/O操作,MySQL資料是分佈在不同的磁軌上的,每次讀取資料都要把所有的磁軌讀取一遍。

那我們進行I/O操作的次數就很多了,查詢效率就很低。

     2:索引就是把索引欄位資料的地址儲存起來,來幫助MySQL直接定位到哪個磁軌哪個扇區,這樣就減少了I/O的操作次數了,自然查詢效率就提高了。

  

  Q2:資料結構那麼多,mysql索引為什麼要用B+Tree資料結構,而不是其他呢?肯定其他的資料不滿足我們的要求

常見的資料結構:

  1:二叉樹

     2:紅黑樹

     3:hash

     4: B Tree

     5: B+Tree

    a.二叉樹

    二叉樹是n(n>=0)節點的有限集合,該集合或者為空集(稱為空二叉樹),或者由一個根節點和二棵不想交的,分別稱為根節點的左子樹和右子樹組成。

      不使用原因:一個節點只有一度,就是隻有一個子節點,那讀取樹的一層就是一次I/O操作,效能也不好。

    b.紅黑樹

   紅黑樹即為平衡二叉樹的一種

      不使用原因:一個節點有二個子節點,那就出現一層只有二個節點的情況,這種效能也不好

    c.hash

      不使用原因:Hash是把索引資料進行Hash演算法對應一個地址,我們會發現這個效能很好啊,直接找到但是我們想想,他能滿足我們日常大部分情況麼?

            比如通過大於或小於篩選資料,所以說也不合適,當然MySQL也提供了Hash索引,畢竟有些場合用起來還是不錯的

BST在節點有序的情況下會變成一種線性結構,複雜度退化到O(n),顯然是不行的。
紅黑樹解決了平衡的問題,但是在資料量比較大的情況下,紅黑樹的高度太高,導致磁碟IO次數過多,也不夠合理。
Hash似乎解決了磁碟IO的問題,但是Hash有大量衝突的時候還是線性遍歷,最關鍵的是限制太多, 例如無法支援範圍查詢,也不支援部分索引匹配。

   d.B Tree

      1.度(Degree)-節點的資料儲存個數 2.葉節點具有相同的深度 3.葉節點的指標為空 4.節點中的資料key從左到右遞增排列

       

    不使用原因:雖然解決了每一層的節點數的極端情況下,但是每一個節點儲存了索引和資料,一層儲存的資料太多也不好,畢竟記憶體能讀取的資料大小就10kb.

    e.B+Tree 【MySQL索引的真正儲存結構

      1.B+Tree(B-Tree的變種)  2.非葉子節點不儲存data,只儲存key,可以增大度  3.葉子節點不儲存指標  4.順序訪問指標,提高區間訪問效能

        

      使用原因:設計有幾個方面

        1:非葉子節點不儲存data,只儲存key,可以增大度

        2:葉子節點不儲存指標

        3:順序訪問指標,提高區間訪問的效能。

比起紅黑樹,上面兩種資料結構都更加矮胖。他們兩之間一個很大的不同是B+樹的節點上不儲存value,只儲存key,

而葉子節點上儲存了所有kv集合,並且節點之間都是有序的。

這樣的好處是每一次磁碟IO能夠讀取的節點更多,也就是樹的度可以設定的更大一些,

因為每次磁碟IO讀取的磁碟頁數是一定的,例如每次磁碟IO能夠讀取1頁=4kb,

那麼省去value的情況下同樣一頁資料能夠讀取更多的key,

這樣就大大減少了磁碟的IO次數。

此外,B+樹是排序好的資料結構,

資料庫中><或者order by等都可以直接依賴這一特性。

  三:B+Tree索引的效能分析

.一般使用磁碟I/O次數評價索引結構的優劣。

   ..預讀:磁碟一般會順序向後讀取一定長度的資料(頁的整倍數)放入記憶體

  ..區域性性原理:當一個數據被用到時,其附近的資料也通常會馬上被使用

  ....B+Tree節點的大小設為等於一個頁,每次新建節點直接申請一個頁的空間,這樣保證一個節點物理上也儲存在一個頁裡,就實現了一個節點的載入只需要一次I/O

  .....B+Tree的度d一般會超過100個,因此h非常小(一般為3到5之間)

  四:不同的儲存引擎,有不同的索引實現

  1:MyISAM索引實現(非聚集)   2.InnoDB索引實現(聚集)

    a.MyISAM索引實現(非聚集)

    ---->MyISAM索引檔案與資料檔案是分離的

      

    b.InnoDB索引實現(聚集)

      1:資料檔案本身就是索引檔案  2:表資料檔案本身就是按B+Tree組織的一個索引結構檔案  3:聚集索引-葉節點包含了完整的資料記錄

      4:為什麼InnoDB表必須有主鍵,並且推薦使用整型的自增主鍵?  5:為什麼非主鍵索引結構葉子節點儲存的是主鍵值?(一致性和節省儲存空間)

        

  五:聯合索引結構

  --->聯合索引的底層儲存結構長什麼樣?

聯合索引底層資料結構長什麼樣?

聯合索引示意

比較相等時,先比較第一列的值,如果相等,再繼續比較第二列,以此類推。

  六:索引最左字首原理

使用聯合索引時,索引列的定義順序將會影響到最終查詢時索引的使用情況。 例如聯合索引(a,b,c),mysql會從最左邊的列優先匹配,如果最左邊的帶頭大哥沒有使用到, 在未使用覆蓋索引的情況下,就只能全表掃描。
聯合底層資料結構思考,mysql會優先以聯合索引第一列匹配, 此後才會匹配下一列,如果不指定第一列匹配的值, 也就無法得知下一步查詢哪個節點。
另外還有一種情況,如果遇到 > < between等這樣的範圍查詢, 那B+樹中也就無法對下一列進行等值匹配了。


單列索引其實也可以看做聯合索引,索引列為1的聯合索引,從下圖就可以看出聯合索引的底層儲存跟單列索引時類似的,區別在於聯合索引是每個樹節點中包含多個索引值,在通過索引查詢記錄時,會先將聯合索引中第一個索引列與節點中第一個索引值進行匹配,匹配成功接著匹配第二個索引列和索引值,直到聯合索引的所有索引列都匹配完;如果過程中出現某一個索引列與節點相應位置的索引值不匹配的情況,則無需再匹配節點中剩餘索引列,前往下一個節點。

為什麼mysql頁檔案預設16K?

MySQL每個B+樹節點最大儲存容量:16KB (指標+資料+索引)。假設我們一行資料大小為1K,那麼一頁就能存16條資料,也就是一個葉子節點能存16條資料;再看非葉子節點,假設主鍵ID為bigint型別,那麼長度為8B,指標大小在Innodb原始碼中為6B,一共就是14B,那麼一頁裡就可以儲存16K/14=1170個(主鍵+指標)
那麼一顆高度為2的B+樹能儲存的資料為:117016=18720條,一顆高度為3的B+樹能儲存的資料為:11701170*16=21902400(千萬級條)

show global status like `Innodb_page_size`

因此,B+樹儲存大資料量的表也可以非常高效的獲取資料,MySQL使用B+樹作為索引的資料結構。

儲存引擎

儲存引擎最終作用於:表 ,不是資料庫
在mysql的安裝的根目錄下,有一個data目錄,裡面存放的是所有表的資料。

  • MyISAM:
    MyISAM索引檔案和資料檔案是分離的(非聚集或稀疏);
    主鍵索引和輔助主鍵索引儲存類似;

frm檔案:儲存這張表的表結構
MYD檔案:儲存這張表的所有資料行
MYI檔案:儲存這張表的索引欄位

  • InnoDB(聚集):

表資料檔案本身是按照B+tree組織的一個索引結構檔案
frm檔案:儲存這張表的表結構
ibd檔案:儲存這張表的所有資料行和索引欄位
聚集(聚簇)索引----葉節點包含完整資料記錄

為什麼InnoDB表必須有主鍵,並且推薦使用整型的自增主鍵?

  • 首先,為了滿足MySQL的索引資料結構B+樹的特性,必須要有索引作為主鍵,可以有效提高查詢效率,因此InnoDB必須要有主鍵。如果不手動指定主鍵,InnoDB會從插入的資料中找出不重複的一列作為主鍵索引,如果沒找到不重複的一列,InnoDB會在後臺增加一列rowId做為主鍵索引。
  • 其次,索引的資料型別是整型,一方面整型佔有的磁碟空間或記憶體空間相比字串更少,另一方面整型比較比字串比較更快速,字串比較是先轉換為ASCII碼,然後再比較的。
  • 最後,B+樹本質是多路多叉樹,如果主鍵索引不是自增的,那麼後續插入的索引就會引起B+樹的其他節點的分裂和重新平衡,影響資料插入的效率,如果是自增主鍵,只用在尾節點做增加就可以。

為什麼非主鍵索引結構葉子節點儲存的是主鍵值?

  • 主鍵索引和非主鍵索引維護各自的B+樹結構,當插入的資料的時候,由於資料只有一份,通過非主鍵索引獲取到主鍵值,然後再去主鍵索引的B+樹資料結構中找到對應的行資料,節省了記憶體空間;
  • 如果非主鍵索引的葉子節點也儲存一份資料,如果通過非主鍵索引插入資料,那麼要向主鍵索引對應的行資料進行同步,那麼會帶來資料一致性問題。可以通過事務的方式解決,我們都知道使用事務後,就會對效能有所消耗。

摘錄自

https://www.cnblogs.com/xiaoqiang-code/p/11386239.html

https://www.cnblogs.com/qiupuwork/p/11046531.html

https://www.jianshu.com/p/fee062c115ea