日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

大话InnoDB索引原理

發布時間:2024/1/17 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大话InnoDB索引原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

B樹,對于從事數據庫行業的技術人員來說,是必須要掌握的知識。我研究生時期的一個在國內足夠權威的數據庫老師說過,“我想不通為什么在《數據庫原理》這門數據庫必修課的教學大綱中,竟然沒有將B樹這章列入其中,這是多么荒唐的事情”,從這句話也可以看出,B樹對于數據庫而言,是多么重要的一個技術。

或者換一個概念,我們所熟悉的——索引,是我們日常工作必須要打交道的,因為我們所了解的大多數數據庫,其索引都是通過B樹或類B樹實現的,今天的主題InnoDB,正是其中一員,所以從這個角度來看,索引和B樹,就可以等同起來,不過更加精確的講,InnoDB是使用B+樹來實現其索引功能的。具體到B+樹和B樹的區別,如上所言,這是應該在大學期間學習到的,如果真的沒有學過,也沒太大關系,此時還有機會,繼續往下看。

B+樹及B樹區別

首先,關于什么是B樹,或者B+樹,這些內容教科書或者網上有很多,可以直接獲取到,但他們之間的區別,一般都是理論方面的,以數據庫應用的角度去解釋可能更加容易理解,通過總結,這兩種數據結構的不同之處有如下五點:

1.B樹中同一鍵值不會出現多次,它有可能出現在葉子節點,也有可能出現在內節點中;而B+樹的鍵一定會出現在葉子節點,同時有可能在非葉子節點中也重復出現,簡單來說,B+樹的內節點存儲的都是鍵值,鍵值對應的具體數據都存儲在葉子節點中。

2.由于B樹的每一個節點都存儲了真實的數據,這會導致每一個節點存儲的數據量變小,那么整個B樹,層數就會相對變高,在數據量大了之后,維護代價是比較大的,而且層數越高,搜索或修改的性能就會越低;而B+樹的內節點中,只存儲鍵值,相對而言,一個內節點存儲記錄個數比B樹多很多,隨著B+樹中數據量的增長,由于它是橫向擴展的,最終會成長為一個矮胖子,不像B樹一樣是縱向擴展,最終只會變成一個瘦高個子,這樣整體而言,B+樹在搜索時,從上到下直到葉子節點只需要遍歷層數個節點而已,性能就會比較高。

3.B樹的查詢效率與鍵在B樹中的位置有關,最大時間復雜度與B+樹相同(在葉子節點的時候),最小時間復雜度為1(在根節點的時候);而B+樹的復雜度對某建成的樹是固定的。

4.B樹鍵位置不定,且在整個樹結構中只出現一次,雖然可以節省存儲空間,但這使得插入、刪除等操作復雜度明顯增加。而且性能不平衡,有可能會很快的找到了合適的位置,也有可能需要做比較多的IO操作才能找到。而B+樹相對來說是一種較好的折中,因為內節點相對葉子節點而言,是一個索引的功能,在插入過程中,只需要通過在每一層搜索一個節點,依次找到葉子節點之后,在葉子節點做插入操作即可,只是在遇到一個節點存儲滿了的情況下會進行B+樹分裂,但總體而言性能還是比較穩定。

5.B樹中,所有數據都只存儲一份;而B+樹中,除了存儲了所有數據的葉子節點外,還有只存儲鍵值數據的內節點,所以占用空間量方面,B+樹比B樹會多占用一些空間,這部分空間就是B+樹的內節點的所有空間,但B+樹通過這種方式提高了整體性能,更適合于性能要求很高的文件檢索。

索引的設計

數據庫是用來存儲數據的工具,存進去,是為了更方便的取出來,并且是越快越好,那么性能的要求就非常高了。在計算機上面運行一個任務,涉及到性能的一般有三個部分,分別是內存大小,CPU及磁盤的速度等,而索引是一個存儲的方式,與它相關的最重要的部分就是磁盤了,所以磁盤的性能高低,直接影響了在數據庫查找數據的效率。

磁盤的性能與讀寫順序有關,針對我們普通的機械硬盤而言,順序讀寫會比隨機讀寫快很多,這里涉及到的機械硬盤存儲原理有很多相關書籍介紹,就不再贅述了。那我們在設計如何加快數據庫中數據的讀寫速度時,需要盡可能的避免隨機讀寫,也就是說盡可能的讀取連續的數據,這樣性能自然就會好一些。

除了硬盤的讀寫順序影響之外,還需要考慮讀寫操作本身的效率,因為在讀寫時,有些數據是必須要操作的,也就是我們真正需要的那部分數據,這被稱為有效數據,這部分數據之外的被同時讀寫的數據,被稱為無效數據。索引的設計,必須要盡可能的降低無效數據的讀寫訪問。

考慮到我們關系型數據庫的結構,有如下特點:

  • 數據都是以行為單位一行行存放的,一行中包括一個表(聚簇索引)中或者一個索引(二級索引)中定義的所有列,多行數據可以連續一起存儲。

  • 一行數據中,一般都會有一個鍵,同時有其它附屬的列,我們可以稱之為值,那簡單可以理解為一行數據就是一個鍵值對。可能有些人有一個疑問,如果不定義主鍵怎么辦,沒關系,這個InnoDB已經替你想到了,它會在內部加上一個主鍵,即我們所熟知的RowID值,這樣也就有了一個默認的鍵,相應的表中所有用戶所定義的列就是值了,也照樣形成了一個鍵值對。

  • 鍵值對中,鍵值是可以排序的,也可以是組合鍵值。

  • 綜合上面的特性,再綜合B+樹的特性,這哥倆感覺是天生的一對兒,所以設計存儲方式如下:

  • 磁盤空間,或者存儲文件被劃分為許多大小相同的塊兒(Block)或者頁(Page),而在一個塊兒中,可以存儲多個數據行,這多個數據行在一個塊內的存儲格式可以先不考慮,但這樣設計就是迎合了磁盤順序讀取性能比較高的特性,因為讀取一條數據時,也很有可能讀取其周邊數據,這對于相對固定的塊來說,一次順序IO就可以讀取很多數據出來,在性能提高上是非常適合的,而如果不通過塊來管理行的話,行為單位的管理會非常碎,IO會非常隨機,性能就變得差了。

  • 在一個塊兒內,所有數據行的組織管理也是需要講究的,因為數據有可能經常會變,并且它的大小是相對固定的,有可能會存儲滿,所以內部是通過鏈表或數組的方式來管理的。

  • 上面已經提到,每行數據就是一個鍵值對,并且鍵可以排序,在一個塊內,所有的行數據也可以有序,這樣利用經典的二分查找算法就可以很快的根據指定的鍵來找到對應的鍵值對數據或者一個范圍的很多數據。

  • 一個塊的問題解決之后,如何形成一顆B+樹呢?現在很容易想到,可以讓一個塊作為一個B+樹的節點,這樣通過塊來承載數據,通過B+樹這個數據結構來組織不同塊之間的關系,最終形成一個矮矮胖胖的B+樹結構。

  • 因為行是一個鍵值對,而B+樹的特點是通過在內節點中只存儲鍵來提高搜索性能,這兩個特性正好匹配。很自然的,在B+樹中,內節點存儲了行數據中的鍵,而葉子節點存儲所有的行數據。通過內節點的鍵值及一個位置信息,內節點與下層節點或者葉子節點之間的指針,就可以找到其孩子節點了。

  • 現在這個B+樹(索引)雛形就出來了,再結合其它章節所講的InnoDB文件管理方法,就可以串起來了,這里所說的塊,其實就是一個頁面,B+樹所用到的塊,就是被一個段,或者簇所管理的。

    聚簇索引(Clustered Index)和輔助(二級)索引(Secondary Index)

    我們在查詢數據時,一般都會在經常被查詢的字段上面建立一個索引,這正是利用了索引中被排序的鍵值,通過內節點的索引功能及葉子節點中數據的有序性利用二分查找極大的提高了查找的性能,所以索引在數據庫中的作用是至關重要的。

    在之前章節中介紹表空間文件管理時已經提到,一個表可以建立多個索引,但每一個表都有一個索引是存儲了所有數據的,這個索引我們一般稱之為“聚簇索引”,聚簇索引在一個表中只有一個且是建立在主鍵上面的,這個主鍵所包含的列可以是被隱藏的Rowid列,也可以是自增列,還可以是明確定義的不含NULL值的組合列等,除聚簇索引之外的所有索引,我們都稱之為二級索引,那么很明顯,二級索引可以有多個,并且一般沒有上限,想建多少都可以,不過如果兩個索引建立在同樣的列,或者列組合上面,那這兩個索引我們稱之為重復索引或者冗余索引,這個在MySQL中一般都會以一個警告給出,通過show warnings我們可以看到警告信息如下:


    mysql> show warnings\G
    *************************** 1. row ***************************
    Level: Note
    Code: 1831
    Message: Duplicate index 'its3' defined on the table 'local.ts'. This is deprecated and will be disallowed in a future release.
    1 row in set (0.00 sec)

    從上面的特性我們可以知道,一個表中,聚簇索引占用的空間肯定是最大的,因為它是存儲了全部數據的,而二級索引,是建立在某幾個需要經常查詢的列上面的,除了這幾個列之外,剩下的就是用來“回表”的指針信息了,所以相對而言,二級索引的占用空間都會比聚簇索引小很多,特別是在一個表的列數很多或是這些列中包含大字段的情況下,因為我們一般都不會在大字段上直接建立索引。那這樣比較下來,在我們統計一個表總的精確行數時(查COUNT*),一些優化器就會選擇表中最小的索引來作為統計的目標索引,因為它占用空間最小,IO也會最小,性能相應的更快一些。

    上面說到了“回表”,所謂回表,就是在使用二級索引時,因為二級索引只存儲了部分數據,如果根據鍵值查找到的數據不能包括全部目標數據時,就需要通過二級索引的指針,也就是鍵值對中的值,來找到聚簇索引的全部數據,然后根據完整的數據取出所需要的列的過程。這種在二級索引中不能找到所有需要的數據列的現象,被稱為非覆蓋索引,反之稱為覆蓋索引。因為回表本身是需要去另一個索引(聚簇索引)中查找數據的,性能必然會受到影響,那為了盡可能的提高性能就需要盡量的減少回表次數,所以可以試著將出現頻率非常高的語句中所有使用到的列以合適的順序建一個二級索引,這樣所有需要的列都被這個二級索引覆蓋了,就不需要回表了,從而一定程度上提高了性能。這雖然是一個好的做法,但需要去權衡,因為需要考慮語句中涉及到的列數,這個語句出現的頻率及最終這個索引的大小。最壞的情況是建一個和聚簇索引差不多大的二級索引,這樣一方面是占用空間比較大,另一方面是維護這個二級索引對這個表的整體修改性能也是有影響的,所以各方面都需要去權衡,然后再決定是不是要這樣做。

    上面還說到了,在統計總行數的時候,可以直接使用二級索引來做,是因為有一個很明顯但很重要的前提:每個二級索引與聚簇索引的總行數是一樣的,并且一對一。只不過在每一個索引中,數據行的排序順序不同,可以想象二級索引行與聚簇索引行行之間都有虛線相連,并且二級索引中每一行都有且只有一條虛線指向聚簇索引中的一行數據,而聚簇索引的每一行,都會有相同個數的虛線指進來,這個數目就是二級索引的個數。至于二級索引與聚簇索引究竟是如何連起來的,我們后面會詳細講述。

    關于二級索引的個數,雖然沒有限制,但可以想象一下,任何事物都是有兩面性的:建一個索引,是為了提高性能,但這是以損失寫入性能為代價的。因為所有索引,在表需要寫入數據時,都需要去維護索引數據以保證所有索引都是最新、最準確的,那可想而知,索引越多,寫入性能也就越差,對索引上鎖的時間也會越長。更嚴重的問題是,如果有唯一索引時,為了保證這個唯一特性,每次修改都會去檢查唯一性,在RR隔離級別下,經常會造成死鎖,所以在建索引時一定要仔細權衡,建出來的索引要個個為精,個個有用,這樣才能保證在最大程度提高性能的情況下,最小程度的影響對表的修改。

    二級索引的指針

    現在已經知道,聚簇索引存儲了所有數據,二級索引只存儲了部分數據,但二級索引是為了提高性能的,所以經常會被使用到,那如果二級索引中的數據不能滿足需求怎么辦?這就用到了我們上面提到的“回表”,也就是二級索引中每行記錄中指針的作用。

    關于聚簇索引及二級索引列之間的邏輯關系,我們分類如下:

  • 自定義主鍵的聚簇索引:

  • 索引結構:[主鍵列][TRXID][ROLLPTR][其它建表創建的非主鍵列]

  • 參與記錄比較的列:主鍵列

  • 內結點KEY列:[主鍵列]+PageNo指針

  • 未定義主鍵的聚簇索引:

  • 索引結構:[ROWID][TRXID][ROLLPTR][其它建表創建的非主鍵列]

  • 參與記錄比較的列:只ROWID一列而已

  • 內結點KEY列:[ROWID]+PageNo指針

  • 自定義主鍵的二級唯一索引:

  • 索引結構:[唯一索引列][主鍵列]

  • 參與記錄比較的列:[唯一索引列][主鍵列]

  • 內結點KEY列:[唯一索引列]+PageNo指針

  • 自定義主鍵的二級非唯一索引:

  • 索引結構:[非唯一索引列][主鍵列]

  • 參與記錄比較的列:[非唯一索引列][主鍵列]

  • 內結點KEY列:[非唯一索引列][主鍵列]+PageNo指針

  • 未定義主鍵的二級唯一索引:

  • 索引結構:[唯一索引列][ROWID] 參與記錄比較的列:[唯一索引列][ROWID]

  • 內結點KEY列:[唯一索引列]+PageNo指針

  • 未定義主鍵的二級非唯一索引:

  • 索引結構:[非唯一索引列][ROWID] 參與記錄比較的列:[非唯一索引列][ROWID]

  • 內結點KEY列:[非唯一索引列][ROWID]+PageNo指針

  • 通過這六種情況,講清楚了聚簇索引記錄包含的列,二級索引記錄包括的列,以及在非葉子節點中分別包含的列,因為索引是用來檢索數據的,所以還講述了用來檢查記錄時,在二級索引及聚簇索引中,參與比較記錄大小的列分別是什么,唯一索引與非唯一索引的區別等。

    需要注意的一點是,上面講述的索引列的順序關系,與實際索引中記錄的物理存儲不是一回事,記錄的存儲格式是記錄的格式,而這個是索引在內存中是元組的組織關系,這個元組的順序體現的就是每個索引自己的邏輯順序,以什么列建的索引,什么列就會在最前面起到優先排序的作用。

    我們這里特別關注一下二級唯一索引的元組邏輯順序,二級唯一索引中,作為索引本身的索引列,就是我們上面所說的“鍵”,當這個元組需要回表時,在元組中存儲的聚簇索引列信息,就是我們所說的“值”,這樣就形成了鍵值對。而對于二級非唯一索引而言,因為只有索引列本身再加上主鍵列才能保證索引記錄是唯一的,所以這二者合起來才能構成我們所說的“鍵”,而“值”就為空了,也就是說,二級非唯一索引中,在記錄構成方面,非葉結節點只是比葉子節點多了一個PageNo指針信息。

    從上面可以看到,二級索引元組中,首先存儲的就是每個索引定義的索引列,接著就是這條記錄對應的聚簇索引的主鍵列的值,而主鍵列是唯一的,所以二級索引回表時對應的記錄也是唯一的,這樣就形成了一種指針的效果。

    不過有一點需要注意一下,二級索引回表時對應的聚簇索引,如果是用戶自定義的,有可能是自增列,也有可能是有邏輯意義的單列或者組合列的聚簇索引,如果用戶沒有自定義,則InnoDB會自動給聚簇索引分配一個主鍵列,不過是隱藏的列,即我們所熟知的Rowid列。基于此,如果是用戶自定義的聚簇索引,則二級索引指針指向的就是聚簇索引所包含的列,如果沒有自定義主鍵,那該指針就指向Rowid列了。


    總結

    以上是生活随笔為你收集整理的大话InnoDB索引原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。