Mysql從入門到入神之(四)B+樹索引

前言

文本已收錄至個人GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客mysql

絮叨

咱們繼續來探索mysql。前面咱們瞭解了mysql的索引的一些基礎知識,今天咱們來康康B+樹索引git

來複習一下一下昨天的 首先是InnoDB的頁存儲結構,咱們知道 多個不一樣的頁組成的是一個雙向鏈表,而每一個頁裏面的數據行會按主鍵的大小組成一個單向鏈表,而且每4到8個數據組成一個槽,每一個槽存儲在pageDirectoy裏面 ,當咱們要查詢頁的行數據的時候,能夠先定位到頁,而後用2分法定位到槽,而後遍歷槽,來定位到當前行的數據。(大佬畫的圖,你們能夠好好理解一下)github

其中頁a、頁b、頁c ... 頁n 這些頁能夠不在物理結構上相連,只要經過雙向鏈表相關聯便可。

沒有索引下的查找數據的方式

  • 第一種,查詢的是id主鍵的一個肯定值,這個好像還不是那麼難,首先遍歷全部的頁,定位到頁,從頁裏面找到槽,從槽裏面找到當前行,因此這樣說的話,這種若是頁數比較多的話,查詢也會很慢
  • 第二種,也就是咱們說的全表掃描,一個個去遍歷,最後來找到這一行數據,由於這種查詢的會很是的慢,因此呢咱們的索引就派上用場了

InnoDB中的索引方案

  • InnoDB是使用頁來做爲管理存儲空間的基本單位,也就是最多能保證16KB的連續存儲空間,而隨着表中記錄數量的增多,須要很是大的連續的存儲空間才能把全部的目錄項都放下,這對記錄數量很是多的表是不現實的。
  • 咱們時常會對記錄進行增刪,假設咱們把頁中的記錄都刪除了,頁也就沒有存在的必要了,那意味着目錄項也就沒有存在的必要了,這就須要把目錄項後的目錄項都向前移動一下,這種牽一髮而動全身的設計不是什麼好主意~

它是怎麼來實現 ,頁記錄 和用戶用戶記錄 ,它每一個行數據中又一個record_type 這個既能夠表示,頁記錄 和用戶用戶記。它有如下的4種取值方式sql

  • 0:普通的用戶記錄
  • 1:目錄項記錄
  • 2:最小記錄
  • 3:最大記錄

不管是存放用戶記錄的數據頁,仍是存放目錄項記錄的數據頁,咱們都把它們存放到B+樹這個數據結構中了,因此咱們也稱這些數據頁爲節點。從圖中能夠看出來,咱們的實際用戶記錄其實都存放在B+樹的最底層的節點上,這些節點也被稱爲葉子節點或葉節點,其他用來存放目錄項的節點稱爲非葉子節點或者內節點,其中B+樹最上邊的那個節點也稱爲根節點。數據結構

聚簇索引

上面的B+數 自己就是一個主鍵索引 咱們也叫聚簇索引,它有兩個特色post

  • 使用記錄主鍵值的大小進行記錄和頁的排序,這包括三個方面的含義:性能

    • 存放目錄項記錄的頁分爲不一樣的層次,在同一層次中的頁也是根據頁中目錄項記錄的主鍵大小順序排成一個雙向鏈表。 (樹的每一層都是一個雙向鏈表)
    • 各個存放用戶記錄的頁也是根據頁中用戶記錄的主鍵大小順序排成一個雙向鏈表。(最後一層的用戶數據層也是一個雙向鏈表)
    • 頁內的記錄是按照主鍵的大小順序排成一個單向鏈表。(頁內是一個單休鏈表,和一個有着順序的槽目錄)
  • B+樹的葉子節點存儲的是完整的用戶記錄。所謂完整的用戶記錄,就是指這個記錄中存儲了全部列的值(包括隱藏列)。學習

咱們把具備這兩種特性的B+樹稱爲聚簇索引,全部完整的用戶記錄都存放在這個聚簇索引的葉子節點處。這種聚簇索引並不須要咱們在MySQL語句中顯式的使用INDEX語句去建立(後邊會介紹索引相關的語句),InnoDB存儲引擎會自動的爲咱們建立聚簇索引。另外有趣的一點是,在InnoDB存儲引擎中,聚簇索引就是數據的存儲方式(全部的用戶記錄都存儲在了葉子節點),也就是所謂的索引即數據,數據即索引。優化

二級索引

你們有木有發現,上邊介紹的聚簇索引只能在搜索條件是主鍵值時才能發揮做用,由於B+樹中的數據都是按照主鍵進行排序的。那若是咱們想以別的列做爲搜索條件該咋辦呢?難道只能從頭至尾沿着鏈表依次遍歷記錄麼?設計

不,咱們能夠多建幾棵B+樹,不一樣的B+樹中的數據採用不一樣的排序規則。比方說咱們用c2列的大小做爲數據頁、頁中記錄的排序規則,再建一棵B+樹,效果以下圖所示:

其實這個呢,和上面的也差不都就是說這個說子節點存放的是咱們的索引列+咱們的主鍵的數據。若是咱們想要當前那一行的全部數據的話,咱們是須要作一次回表操做的。

聯合索引

咱們也能夠同時以多個列的大小做爲排序規則,也就是同時爲多個列創建索引,比方說咱們想讓B+樹按照c2和c3列的大小進行排序,這個包含兩層含義:

  • 先把各個記錄和頁按照c2列進行排序。
  • 在記錄的c2列相同的狀況下,採用c3列進行排序

相似於這種,就是先把第一個列作好索引,而後再排第二個列,必須按前後順序來,因此咱們所說的前綴索引就是這樣來的。

索引的代價

在熟悉了B+樹索引原理以後,本篇文章的主題是嘮叨如何更好的使用索引,雖然索引是個好東西,可不能亂建,在介紹如何更好的使用索引以前先要了解一下使用這玩意兒的代價,它在空間和時間上都會拖後腿:

  • 空間上的代價

    • 這個是顯而易見的,每創建一個索引都要爲它創建一棵B+樹,每一棵B+樹的每個節點都是一個數據頁,一個頁默認會佔用16KB的存儲空間,一棵很大的B+樹由許多數據頁組成,那但是很大的一片存儲空間呢。
  • 時間上的代價

    • 每次對錶中的數據進行增、刪、改操做時,都須要去修改各個B+樹索引。並且咱們講過,B+樹每層節點都是按照索引列的值從小到大的順序排序而組成了雙向鏈表。不管是葉子節點中的記錄,仍是內節點中的記錄(也就是不管是用戶記錄仍是目錄項記錄)都是按照索引列的值從小到大的順序而造成了一個單向鏈表。而增、刪、改操做可能會對節點和記錄的排序形成破壞,因此存儲引擎須要額外的時間進行一些記錄移位,頁面分裂、頁面回收啥的操做來維護好節點和記錄的排序。若是咱們建了許多索引,每一個索引對應的B+樹都要進行相關的維護操做,這還能不給性能拖後腿麼?

建立高性能索引原則

獨立的列

什麼意思呢?就是咱們where = 後面的條件,必須是獨立的一個列,不能是id+1,這種計算,因此有一個原則就是始終將索引列單獨放在比較符合的一側。

前綴索引

好比一個字符串很長,而後你要給這個字段創建索引,若是說他們前幾個字段的識別度很高了話,就建議創建一個前綴索引。這樣就能夠大大的節省索引空間

多列索引

一個常見的錯誤就是,給每一個列都創建一個索引,這樣是錯誤的,還有就是創建聯合索引的時候的順序是隨便填的,這種方式也是錯誤的。若是你用explain 關鍵字看到了 索引合併的信息,就說明你這個索引看是否可否優化。

選擇合適的索引順序

假設你有2個列要創建組合索引,那麼這個組合索引的列的字段究竟是哪一個先 哪一個後呢?這個是沒有必定的標準的,可是默認的條件是若是你有數量少的字段儘可能是放到前面,在不考慮,分組的條件下,這種狀況確實是比較快的。

覆蓋索引

覆蓋索引的意思就是咱們創建索引的時候,我把須要查詢的條件一塊兒創建一個聯合索引,那麼查詢這些數據的時候,咱們就不須要回表操做了。

儘可能用索引掃描來排序

若是explain中type的結果是index,就說明mysql使用了索引掃描來作排序,

未使用的索引

若是發現有些索引是一直不會使用的索引,建議刪除它。

不可使用索引進行排序的幾種狀況

  • ASC、DESC混用 對於使用聯合索引進行排序的場景,咱們要求各個排序列的排序順序是一致的,也就是要麼各個列都是ASC規則排序,要麼都是DESC規則排序。
  • WHERE子句中出現非排序使用到的索引列

總結

  • 索引並非想建就建,凡事都是有代價的嗎,咱們只能說權衡利弊
  • 索引通用的一些場景
    • 等值查詢
    • 匹配組合索引左邊的索引
    • 匹配組合索引的左邊的索引的範圍查詢
    • 匹配等值查詢和範圍查詢
    • 分組查詢
    • 排序
  • 索引的一些注意事項
    • 只爲搜索,分組,排序的列創建索引
    • 只爲數據的識別度高的列創建索引(例如性別就不建議創建索引)
    • 對於字符串的列,若是它的能創建前綴索引,最好就創建前綴索引
    • 爲了讓頁儘可能減小頁分裂的狀況,最好給主鍵創建自增
    • 刪除沒必要要的索引
    • 若是能用覆蓋索引的儘可能用覆蓋索引,減小回表的次數。

結尾

咱們下章繼續再戰。 文章部份內容出自 MySQL 是怎樣運行的:從根兒上理解 MySQL,

平常求贊

好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見

六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !

相關文章
相關標籤/搜索