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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

B+树索引的使用

發布時間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 B+树索引的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

B+樹索引的使用

  • 索引的代價
  • B+樹索引適用的條件
    • 全值匹配
    • 最左匹配原則
    • 匹配列前綴
    • 匹配范圍值
    • ORDER BY
    • GROUP BY
  • 回表的代價
    • 覆蓋索引

索引的代價


索引雖好,但是不能夠亂建。

  • 空間上的代價
    每個建立一個索引都需要建立一顆B+樹,每棵B+樹的每一個節點都是一個數據頁,一個頁默認會占用16KB的存儲空間,而一顆索引樹由許多頁組成…
  • 時間上的代價
    索引雖然讓我們查詢變得更為快速,我們都知道B+樹每層節點都是按照索引列的值從小到大的順序排序而組成雙向鏈表。無論是葉子節點的記錄,還是內節點的記錄都是按照索引列的值從小到大的順序而形成的一個單向鏈表。我們每次對表中的數據進行CUD操作時,可能會對節點和記錄排序造成破壞,所以存儲引擎需要額外的時間進行移位、頁分裂、頁面回收等操作來維護節點和記錄的順序。對性能影響較大。

B+樹索引適用的條件


以下面的表為例

CREATE TABLE person_info(id INT NOT NULL auto_increment,name VARCHAR(100) NOT NULL,birthday DATE NOT NULL,phone_number CHAR(11) NOT NULL,country varchar(100) NOT NULL,PRIMARY KEY (id),KEY idx_name_birthday_phone_number (name, birthday, phone_number) );

二級索引如圖(記錄結構中保留了name,birthday,phone_number,id):

全值匹配

SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1990-09-27' AND phone_number = '15123983239';

查詢流程如下:

  • 在二級索引中,先按照name列進行排序索引,很快能夠找到name列的值是Ashburn的位置。
  • 在name列相同的記錄里,在按照birthday列進行排序索引,很快就能夠找到name列為Ashburn,birthday列是1990-09-27的記錄。
  • 在name列和birthday值相同的數據中查找,根據phone_number排序索引,最終找到唯一確定的那條記錄。

注意:MySQL中有優化器能夠在查詢之前將我們的SQL語句優化為可按照索引的查詢的形式(當然如果無能夠使用的索引則沒辦法)

最左匹配原則

對于以下三條SQL,為什么第三條索引無法走索引呢?

SELECT * FROM person_info WHERE name = 'Ashburn';SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1990-09-27';SELECT * FROM person_info WHERE birthday = '1990-09-27';

前面我們闡述了查找過程,因為B+樹的數據頁和記錄先是按照name列的值排序的,在name列的值相同的情況下才使用birthday列進行排序,即name列不相同的記錄中birthday的值不確保有序。
如果我們跳過索引的順序直接查找可能的沒辦法走索引的。如果我們想使用聯合索引中盡可能多的列,搜索條件中的各個列必須是聯合索引中從最左邊連續的列
比如說:

SELECT * FROM person_info WHERE name = 'Ashburn' AND phone_number = '15123983239';

這樣的只能用到name列的索引,birthday和phone_number的索引均用不到,理由前面闡述過了。

匹配列前綴

比如我們想查詢url以www.開頭的記錄:

> SELECT * FROM url WHERE name LIKE 'www.%'; +----------------+ | url | +----------------+ | www.baidu.com | | www.google.com | | www.gov.cn | | ... | | www.wto.org | +----------------+

假設我們已經給url列創建了索引,如果我們想查詢以com結尾的網址的話索引條件:WHERE url LIKE '%com',這樣的話我們是無法使用url列索引的。
為什么呢?
牽扯到MySQL中的字符串排序規則,大概是這樣的:

  • 先比較字符串的第一個字符,第一個字符小的那個字符串就比較小。

  • 如果兩個字符串的第一個字符相同,那就再比較第二個字符,第二個字符比較小的那個字符串就比較小。

  • 如果兩個字符串的第二個字符也相同,那就接著比較第三個字符,依此類推。

所以一個排好序的字符串列其實有這樣的特點:

  • 先按照字符串的第一個字符進行排序。

  • 如果第一個字符相同再按照第二個字符進行排序。

  • 如果第二個字符相同再按照第三個字符進行排序,依此類推。

這樣就會有一個問題——我們字符串的前綴的有序排列的,是能夠索引的,所以LIKE 'www.%'能夠走索引,而%.com這樣的因為只有前綴有序,尾綴是com字符串并沒有排好序,所以MySQL無法快速定位記錄的位置,只能夠全表查詢。

對于這樣的數據,我們能夠將數據倒置存儲,這樣就能夠索引moc,如:

+----------------+ | url | +----------------+ | moc.udiab.www | | moc.elgoog.www | | nc.vog.www | | ... | | gro.otw.www | +----------------+

匹配范圍值

回頭看第一個例子,B+樹中所有的記錄都是按照索引列的值從小到大排序,所以這也讓我們的范圍查詢更加的方便了,比如

SELECT * FROM person_info WHERE name > 'Asa' AND name < 'Barlow';

由于B+樹中的數據頁和記錄是先按name列排序的,所以我們上邊的查詢過程其實是這樣的:

  • 找到name值為Asa的記錄。
  • 找到name值為Barlow的記錄。
    由于所有記錄都是由鏈表連起來的(記錄之間用單鏈表,數據頁之間用雙鏈表),所以他們之間的記錄都可以很容易的取出
    找到這些記錄的主鍵值,再到聚簇索引中回表查找完整的記錄。

同樣的也需要遵從最左匹配原則,否則會全表查詢

ORDER BY

需要對查詢出來的記錄通過ORDER BY子句按照某種規則進行排序時,一般情況下,我們只能把記錄都加載到內存中,再用一些排序算法,比如快速排序、歸并排序等等在內存中對這些記錄進行排序,有的時候可能查詢的結果集太大以至于不能在內存中進行排序的話,還可能暫時借助磁盤的空間來存放中間結果,排序操作完成后再把排好序的結果集返回到客戶端。在MySQL中,把這種在內存中或者磁盤上進行排序的方式統稱為文件排序,跟文件這個詞兒一沾邊兒,就顯得這些排序操作非常慢了。
但是如果ORDER BY子句里使用到了索引列,就有可能省去在內存或文件中排序的步驟,比如下邊這個簡單的查詢語句:
ORDER BY默認升序排序

SELECT * FROM person_info ORDER BY name, birthday, phone_number LIMIT 10;

這個查詢的結果集需要先按照name值排序,如果記錄的name值相同,則需要按照birthday來排序,如果birthday的值相同,則需要按照phone_number排序。數據在查詢的過程中已經完成了排序。

對于聯合索引有個問題需要注意:ORDER BY的子句后邊的列的順序也必須按照索引列的順序給出,如果給出ORDER BY phone_number, birthday, name的順序,那用不了B+樹索引。

不可以使用索引的情況:

  • ASC和DESC混用
    對于使用聯合索引進行排序的場景,我們要求各個排序列的排序順序是一致的,也就是要么各個列都是ASC規則排序,要么都是DESC規則排序。
    原因:
    在查詢中的排序順序是一致的情況下,例如:
    ORDER BY name, birthday LIMIT 10 這樣直接從索引的最左邊開始往右讀取10行記錄就可以。
    ORDER BY name DESC, birthday DESC LIMIT 10這樣直接從索引的最右邊開始往左讀10行記錄就可以。
    如果我們先按照name列進行升序排列,再按照birthday列進行降序排列的話,比如說這樣的查詢語句:
SELECT * FROM person_info ORDER BY name, birthday DESC LIMIT 10;

索引過程:

  • 先從索引的最左邊確定name列最小的值,然后找到name列等于該值的所有記錄,然后從name列等于該值的最右邊的那條記錄開始往左找10條記錄。

  • 如果name列等于最小的值的記錄不足10條,再繼續往右找name值第二小的記錄,重復上邊那個過程,直到找到10條記錄為止。

  • 排序列使用了復雜的表達式
    要想使用索引進行排序操作,必須保證索引列是以單獨列的形式出現,而不是修飾過的形式,比方說這樣:

SELECT * FROM person_info ORDER BY UPPER(name) LIMIT 10;

使用了UPPER函數修飾過的列就不是單獨的列了,這樣就無法使用索引進行排序。

GROUP BY

有時候我們為了方便統計表中的一些信息,會把表中的記錄按照某些列進行分組。比如下邊這個分組查詢:

SELECT name, birthday, phone_number, COUNT(*) FROM person_info GROUP BY name, birthday, phone_number

這個查詢語句相當于做了3次分組操作:

  • 先把記錄按照name值進行分組,所有name值相同的記錄劃分為一組。

  • 將每個name值相同的分組里的記錄再按照birthday的值進行分組,將birthday值相同的記錄放到一個小分組里,所以看起來就像在一個大分組里又化分了好多小分組。

  • 再將上一步中產生的小分組按照phone_number的值分成更小的分組,所以整體上看起來就像是先把記錄分成一個大分組,然后把大分組分成若干個小分組,然后把若干個小分組再細分成更多的小小分組。

然后針對那些小小分組進行統計,比如在我們這個查詢語句中就是統計每個小小分組包含的記錄條數。如果沒有索引的話,這個分組過程全部需要在內存里實現,而如果有了索引的話,恰巧這個分組順序又和我們的B+樹中的索引列的順序是一致的,而我們的B+樹索引又是按照索引列排好序的,這不正好么,所以可以直接使用B+樹索引進行分組。

和使用B+樹索引進行排序是一個道理,分組列的順序也需要和索引列的順序一致,也可以只使用索引列中左邊的列進行分組

回表的代價


例如這個查詢:

SELECT * FROM person_info WHERE name > 'Asa' AND name < 'Barlow';

在使用二級索引進行查詢時大致可以分為兩個步驟:

  • 從索引對應的B+樹中取出name值在Asa~Barlow之間的用戶記錄。

  • 由于索引對應的B+樹用戶記錄中只包含name、birthday、phone_number、id這4個字段,而查詢列表是*,意味著要查詢表中所有字段,也就是還要包括country字段。這時需要把從上一步中獲取到的每一條記錄的id字段都到聚簇索引對應的B+樹中找到完整的用戶記錄,也就是我們通常所說的回表,然后把完整的用戶記錄返回給查詢用戶。

  • 由于二級索引對應的B+樹會按照name列的值進行排序,所以值在Asa~Barlow之間的記錄在磁盤中的存儲是相連的,集中分布在一個或幾個數據頁中,我們可以很快的把這些記錄從磁盤讀取出來,這種讀取方式稱為順序I/O,而我們根據二級索引獲取的主鍵是不連續的,把這些不連續的主鍵值到聚簇索引中訪問完整的數據記錄,但是完整的用戶記錄很可能分布在不同的數據頁中,這樣讀取完整的用戶記錄可能要訪問更多的數據頁,這種讀取方式我們也可以稱為隨機I/O,一般情況下,順序I/O比隨機I/O性能高很多.
    所以這個使用索引的查詢有這么兩個特點:

  • 會使用到兩個B+樹索引,一個二級索引,一個聚簇索引。
  • 訪問二級索引使用順序I/O,訪問聚簇索引使用隨機I/O。
  • 需要回表的記錄越多,使用二級索引的性能就越低 甚至讓某些查詢寧愿使用全表掃描也不使用二級索引。

    那什么時候采用全表掃描的方式,什么時候使用采用二級索引 + 回表的方式去執行查詢呢?這個就是傳說中的查詢優化器做的工作,查詢優化器會事先對表中的記錄計算一些統計數據,然后再利用這些統計數據根據查詢的條件來計算一下需要回表的記錄數,需要回表的記錄數越多,就越傾向于使用全表掃描,反之傾向于使用二級索引 + 回表的方式。

    覆蓋索引

    為了解決回表帶來的性能損耗,最好在查詢列表中只包含索引列
    比如:

    SELECT name, birthday, phone_number FROM person_info WHERE name > 'Asa' AND name < 'Barlow'

    因為我們查詢的是索引列,所以索引得到的結果就不必到聚簇索引中再查找記錄的剩余列。

    總結

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

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