【深入學習MySQL】MySQL的索引結構為什麼使用B+樹?

来源:https://www.cnblogs.com/kismetv/archive/2019/09/25/11582214.html
-Advertisement-
Play Games

在MySQL中,無論是Innodb還是MyIsam,都使用了B+樹作索引結構(這裡不考慮hash等其他索引)。本文將從最普通的二叉查找樹開始,逐步說明各種樹解決的問題以及面臨的新問題,從而說明MySQL為什麼選擇B+樹作為索引結構。 ...


前言

在MySQL中,無論是Innodb還是MyIsam,都使用了B+樹作索引結構(這裡不考慮hash等其他索引)。本文將從最普通的二叉查找樹開始,逐步說明各種樹解決的問題以及面臨的新問題,從而說明MySQL為什麼選擇B+樹作為索引結構。

目錄

一、二叉查找樹(BST):不平衡

二、平衡二叉樹(AVL):旋轉耗時

三、紅黑樹:樹太高

四、B樹:為磁碟而生

五、B+樹

六、感受B+樹的威力

七、總結

一、二叉查找樹(BST):不平衡

二叉查找樹(BST,Binary Search Tree),也叫二叉排序樹,在二叉樹的基礎上需要滿足:任意節點的左子樹上所有節點值不大於根節點的值,任意節點的右子樹上所有節點值不小於根節點的值。如下是一顆BST(圖片來源)。

當需要快速查找時,將數據存儲在BST是一種常見的選擇,因為此時查詢時間取決於樹高,平均時間複雜度是O(lgn)。然而,BST可能長歪而變得不平衡,如下圖所示(圖片來源),此時BST退化為鏈表,時間複雜度退化為O(n)。

為瞭解決這個問題,引入了平衡二叉樹。

二、平衡二叉樹(AVL):旋轉耗時

AVL樹是嚴格的平衡二叉樹,所有節點的左右子樹高度差不能超過1;AVL樹查找、插入和刪除在平均和最壞情況下都是O(lgn)。

AVL實現平衡的關鍵在於旋轉操作:插入和刪除可能破壞二叉樹的平衡,此時需要通過一次或多次樹旋轉來重新平衡這個樹。當插入數據時,最多只需要1次旋轉(單旋轉或雙旋轉);但是當刪除數據時,會導致樹失衡,AVL需要維護從被刪除節點到根節點這條路徑上所有節點的平衡,旋轉的量級為O(lgn)。

由於旋轉的耗時,AVL樹在刪除數據時效率很低;在刪除操作較多時,維護平衡所需的代價可能高於其帶來的好處,因此AVL實際使用並不廣泛。

三、紅黑樹:樹太高

與AVL樹相比,紅黑樹並不追求嚴格的平衡,而是大致的平衡:只是確保從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。從實現來看,紅黑樹最大的特點是每個節點都屬於兩種顏色(紅色或黑色)之一,且節點顏色的劃分需要滿足特定的規則(具體規則略)。紅黑樹示例如下(圖片來源):

 

與AVL樹相比,紅黑樹的查詢效率會有所下降,這是因為樹的平衡性變差,高度更高。但紅黑樹的刪除效率大大提高了,因為紅黑樹同時引入了顏色,當插入或刪除數據時,只需要進行O(1)次數的旋轉以及變色就能保證基本的平衡,不需要像AVL樹進行O(lgn)次數的旋轉。總的來說,紅黑樹的統計性能高於AVL。

因此,在實際應用中,AVL樹的使用相對較少,而紅黑樹的使用非常廣泛。例如,Java中的TreeMap使用紅黑樹存儲排序鍵值對;Java8中的HashMap使用鏈表+紅黑樹解決哈希衝突問題(當衝突節點較少時,使用鏈表,當衝突節點較多時,使用紅黑樹)。

對於數據在記憶體中的情況(如上述的TreeMap和HashMap),紅黑樹的表現是非常優異的。但是對於數據在磁碟等輔助存儲設備中的情況(如MySQL等資料庫),紅黑樹並不擅長,因為紅黑樹長得還是太高了。當數據在磁碟中時,磁碟IO會成為最大的性能瓶頸,設計的目標應該是儘量減少IO次數;而樹的高度越高,增刪改查所需要的IO次數也越多,會嚴重影響性能。

四、B樹:為磁碟而生

B樹也稱B-樹(其中-不是減號),是為磁碟等輔存設備設計的多路平衡查找樹,與二叉樹相比,B樹的每個非葉節點可以有多個子樹。因此,當總節點數量相同時,B樹的高度遠遠小於AVL樹和紅黑樹(B樹是一顆“矮胖子”),磁碟IO次數大大減少。

定義B樹最重要的概念是階數(Order),對於一顆m階B樹,需要滿足以下條件:

  • 每個節點最多包含 m 個子節點。
  • 如果根節點包含子節點,則至少包含 2 個子節點;除根節點外,每個非葉節點至少包含 m/2 個子節點。
  • 擁有 k 個子節點的非葉節點將包含 k - 1 條記錄。
  • 所有葉節點都在同一層中。

可以看出,B樹的定義,主要是對非葉結點的子節點數量和記錄數量的限制。

下圖是一個3階B樹的例子(圖片來源):

 

B樹的優勢除了樹高小,還有對訪問局部性原理的利用。所謂局部性原理,是指當一個數據被使用時,其附近的數據有較大概率在短時間內被使用。B樹將鍵相近的數據存儲在同一個節點,當訪問其中某個數據時,資料庫會將該整個節點讀到緩存中;當它臨近的數據緊接著被訪問時,可以直接在緩存中讀取,無需進行磁碟IO;換句話說,B樹的緩存命中率更高。

B樹在資料庫中有一些應用,如mongodb的索引使用了B樹結構。但是在很多資料庫應用中,使用了是B樹的變種B+樹。

五、B+樹

B+樹也是多路平衡查找樹,其與B樹的區別主要在於:

  • B樹中每個節點(包括葉節點和非葉節點)都存儲真實的數據,B+樹中只有葉子節點存儲真實的數據,非葉節點只存儲鍵。在MySQL中,這裡所說的真實數據,可能是行的全部數據(如Innodb的聚簇索引),也可能只是行的主鍵(如Innodb的輔助索引),或者是行所在的地址(如MyIsam的非聚簇索引)。
  • B樹中一條記錄只會出現一次,不會重覆出現,而B+樹的鍵則可能重覆重現——一定會在葉節點出現,也可能在非葉節點重覆出現。
  • B+樹的葉節點之間通過雙向鏈錶鏈接。
  • B樹中的非葉節點,記錄數比子節點個數少1;而B+樹中記錄數與子節點個數相同。

由此,B+樹與B樹相比,有以下優勢:

  • 更少的IO次數:B+樹的非葉節點只包含鍵,而不包含真實數據,因此每個節點存儲的記錄個數比B數多很多(即階m更大),因此B+樹的高度更低,訪問時所需要的IO次數更少。此外,由於每個節點存儲的記錄數更多,所以對訪問局部性原理的利用更好,緩存命中率更高。
  • 更適於範圍查詢:在B樹中進行範圍查詢時,首先找到要查找的下限,然後對B樹進行中序遍歷,直到找到查找的上限;而B+樹的範圍查詢,只需要對鏈表進行遍歷即可。
  • 更穩定的查詢效率:B樹的查詢時間複雜度在1到樹高之間(分別對應記錄在根節點和葉節點),而B+樹的查詢複雜度則穩定為樹高,因為所有數據都在葉節點。

B+樹也存在劣勢:由於鍵會重覆出現,因此會占用更多的空間。但是與帶來的性能優勢相比,空間劣勢往往可以接受,因此B+樹的在資料庫中的使用比B樹更加廣泛。

六、感受B+樹的威力

前面說到,B樹/B+樹與紅黑樹等二叉樹相比,最大的優勢在於樹高更小。實際上,對於Innodb的B+索引來說,樹的高度一般在2-4層。下麵來進行一些具體的估算。

樹的高度是由階數決定的,階數越大樹越矮;而階數的大小又取決於每個節點可以存儲多少條記錄。Innodb中每個節點使用一個頁(page),頁的大小為16KB,其中元數據只占大約128位元組左右(包括文件管理頭信息、頁面頭信息等等),大多數空間都用來存儲數據。

  • 對於非葉節點,記錄只包含索引的鍵和指向下一層節點的指針。假設每個非葉節點頁面存儲1000條記錄,則每條記錄大約占用16位元組;當索引是整型或較短的字元串時,這個假設是合理的。延伸一下,我們經常聽到建議說索引列長度不應過大,原因就在這裡:索引列太長,每個節點包含的記錄數太少,會導致樹太高,索引的效果會大打折扣,而且索引還會浪費更多的空間。
  • 對於葉節點,記錄包含了索引的鍵和值(值可能是行的主鍵、一行完整數據等,具體見前文),數據量更大。這裡假設每個葉節點頁面存儲100條記錄(實際上,當索引為聚簇索引時,這個數字可能不足100;當索引為輔助索引時,這個數字可能遠大於100;可以根據實際情況進行估算)。

對於一顆3層B+樹,第一層(根節點)有1個頁面,可以存儲1000條記錄;第二層有1000個頁面,可以存儲1000*1000條記錄;第三層(葉節點)有1000*1000個頁面,每個頁面可以存儲100條記錄,因此可以存儲1000*1000*100條記錄,即1億條。而對於二叉樹,存儲1億條記錄則需要26層左右。

七、總結

最後,總結一下各種樹解決的問題以及面臨的新問題:

1)       二叉查找樹(BST):解決了排序的基本問題,但是由於無法保證平衡,可能退化為鏈表;

2)       平衡二叉樹(AVL):通過旋轉解決了平衡的問題,但是旋轉操作效率太低;

3)       紅黑樹:通過捨棄嚴格的平衡和引入紅黑節點,解決了AVL旋轉效率過低的問題,但是在磁碟等場景下,樹仍然太高,IO次數太多;

4)       B樹:通過將二叉樹改為多路平衡查找樹,解決了樹過高的問題;

5)       B+樹:在B樹的基礎上,將非葉節點改造為不存儲數據的純索引節點,進一步降低了樹的高度;此外將葉節點使用指針連接成鏈表,範圍查詢更加高效。

參考文獻

《MySQL技術內幕:InnoDB存儲引擎》

《MySQL運維內參》

https://zhuanlan.zhihu.com/p/54102723

https://cloud.tencent.com/developer/article/1425604

https://blog.csdn.net/whoamiyang/article/details/51926985

https://www.jianshu.com/p/37436ed14cc6

https://blog.csdn.net/CrankZ/article/details/83301702

https://www.cnblogs.com/gaochundong/p/btree_and_bplustree.html

 

創作不易,如果文章對你有幫助,就點個贊、評個論唄~

創作不易,如果文章對你有幫助,就點個贊、評個論唄~

創作不易,如果文章對你有幫助,就點個贊、評個論唄~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、查看mysql安裝目錄 從目錄etc/my.cnf中查看安裝目錄 2、進入mysql目錄,停止mysql服務 命令: cd usr/local/mysql 命令:service mysql stop 3、移動整個mysql目錄 命令:cp -rf /usr/local/mysql/ home/m ...
  • 利用Mysql函數和過程,製作一個數據量能到千萬級的數據表;併在此表上驗證覆蓋索引對查詢效率的影響。 ...
  • 當備份的失敗,出現說什麼應該支持多少個介質簇,但實際出現了多少介質簇,這個時候就要考慮備份的地址是不是出現問題。 首先,檢查備份地址,是不是多於兩個以上,那麼在備份的時候應該註意,備份地址最好留一個,就不會出現這個問題。 具體應該怎麼備份呢? 選擇要備份的資料庫,然後清空你不想要的備份地址,選擇你想 ...
  • 如果YourSQLDba設置過共用路徑備份(具體參考博客YourSQLDba設置共用路徑備份),有時候伺服器重啟後,備份就會出錯,具體錯誤信息類似如下所示: Date 2019/9/25 10:10:00Log SQL Server (Current - 2019/9/25 3:06:00) Sou... ...
  • 腳本需求: 每天備份mysql資料庫,保留7天的腳本。 存放在/opt/dbbak目錄中。 腳本名稱為database_xxxx-xx-xx.sql 腳本內容: #!/bin/bash export NOW="$(date +"%Y-%m-%d")" export DATA_DIR=/opt/dbb ...
  • 數據傾斜特征:個別Task處理大部分數據 後果:1.OOM;2.速度變慢,甚至變得慢的不可接受 常見原因: 數據傾斜的定位: 1.WebUI(查看Task運行的數據量的大小)。 2.Log,查看log中哪一行出現OOM,查找具體哪個Stage,進而確定哪一個shuffle產生了數據傾斜。 3.查看代 ...
  • vi dbbackup.sh在打開的編輯器輸入: 命令的意思是用mysqldump導出名為databasename的資料庫到/home/wwwroot/backup/文件夾並命名為date_日期.sql,-u後面的是你的Mysql的用戶名,-p後面的是Mysql密碼,databasename是要備份 ...
  • HAVING 子句 在 SQL 中增加 HAVING 子句原因是,WHERE 關鍵字無法與合計函數一起使用。 SQL HAVING 語法 SQL HAVING 實例 我們擁有下麵這個 "Orders" 表: O_IdOrderDateOrderPriceCustomer 1 2008/12/29 1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...