搜尋
首頁資料庫mysql教程MySQL的InnoDB索引原理詳解

MySQL的InnoDB索引原理詳解

Feb 23, 2017 am 11:11 AM
innodbmysql


 摘要:

  本篇介紹下Mysql的InnoDB索引相關知識,從各種樹到索引原理到儲存的細節。

  InnoDB是Mysql的預設儲存引擎(Mysql5.5.5之前是MyISAM,文檔)。本著高效學習的目的,本篇以介紹InnoDB為主,少量涉及MyISAM作為對比。

  這篇文章是我在學習過程中總結完成的,內容主要來自書本和博客(參考文獻會給出),過程中加入了一些自己的理解,描述不准確的地方煩請指出。

  1 各種樹形結構

  本來不打算從二元搜尋樹開始,因為網路上已經有太多相關文章,但是考慮到清晰的圖示對理解問題有很大幫助,也為了確保文章完整性,最後還是加上了這部分。

  先看看幾種樹形結構:

  1 搜尋二元樹:每個節點有兩個子節點,資料量的增大必然導致高度的快速增加,顯然這個不適合作為大量資料儲存的基礎結構。

  2 B樹:一棵m階B樹是一棵平衡的m路搜尋樹。最重要的性質是每個非根節點所包含的關鍵字個數j 滿足:┌m/2┐ - 1

  3 B+樹:一棵m階B樹是一棵平衡的m路搜尋樹。最重要的性質是每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1

  4 B*樹:一棵m階B樹是一棵平衡的m路搜尋樹。最重要的兩個性質是1每個非根節點所包含的關鍵字個數 j 滿足:┌m2/3┐ - 1

#  B/B+/B*三種樹有類似的操作,例如擷取/插入/刪除節點。這裡只重點關注插入節點的情況,且只分析他們在當前節點已滿情況下的插入操作,因為這個動作稍微複雜且能充分體現幾種樹的差異。與之比較的是檢索節點比較容易實現,而刪除節點只要完成與插入相反的過程即可(在實際應用中刪除並不是插入的完全逆操作,往往只刪除資料而保留下空間為後續使用)。

  先看B樹的分裂,下圖的紅色值即為每次新插入的節點。每當一個節點滿後,就需要發生分裂(分裂是一個遞歸過程,參考下面7的插入導致了兩層分裂),由於B樹的非葉子節點同樣保存了鍵值,所以已滿節點分裂後的值將分佈在三個地方:1原節點,2原節點的父節點,3原節點的新兄弟節點(參考5,7的插入過程)。分裂有可能導致樹的高度增加(參考3,7的插入過程),也可能不影響樹的高度(參考5,6的插入過程)。

  B+樹的分割:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂只影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟節點的指標。

  B*樹的分割:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了)。如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的數據到新結點,最後在父結點增加新結點的指針。可以看到B*樹的分裂非常巧妙,因為B*樹要確保分裂後的節點還要2/3滿,如果採用B+樹的方法,只是簡單的將已滿的節點一分為二,會導致每個節點只有1/2滿,這不符合B*樹的要求了。所以B*樹採取的策略是在本節點滿後,繼續插入兄弟節點(這也是為什麼B*樹需要在非葉子節點加一個兄弟間的鍊錶),直到把兄弟節點也塞滿,然後拉上兄弟節點一起湊份子,自己和兄弟節點各出資1/3成立新節點,這樣的結果是3個節點剛好是2/3滿,達到B*樹的要求,皆大歡喜。

  B+樹適合作為資料庫的基礎結構,完全是因為電腦的記憶體-機械硬碟兩層儲存結構。記憶體可以完成快速的隨機存取(隨機存取即給出任一個位址,要求傳回這個位址儲存的資料)但是容量較小。而硬碟的隨機存取要經過機械動作(1磁頭移動 2碟片轉動),存取效率比記憶體低幾個數量級,但是硬碟容量較大。典型的資料庫容量大大超過可用記憶體大小,這決定了在B+樹中檢索一條資料很可能要藉助幾次磁碟IO操作來完成。如下圖所示:通常向下讀取一個節點的動作可能會是一次磁碟IO操作,不過非葉節點通常會在初始階段載入記憶體以加快存取速度。同時為提高在節點間橫向遍歷速度,真實資料庫中可能會將圖中藍色的CPU運算/記憶體讀取最佳化成二元搜尋樹(InnoDB中的page directory機制)。

  真實資料庫中的B+樹應該是非常扁平的,可以透過向表中順序插入足夠資料的方式來驗證InnoDB中的B+樹到底有多扁平。我們透過如下圖的CREATE語句建立一個只有簡單欄位的測試表,然後不斷新增資料來填入這個表。透過下圖的統計資料(資料來源見參考文獻1)可以分析出幾個直觀的結論,這幾個結論宏觀的展現了資料庫裡B+樹的尺度。

  1 每個葉子節點儲存了468行數據,每個非葉子節點儲存了大約1200個鍵值,這是一棵平衡的1200路搜尋樹!

  2 對於一個22.1G容量的表,也只需要高度為3的B+樹就能儲存了,這個容量大概能滿足很多應用的需要了。如果把高度增加到4,則B+樹的儲存容量立刻增加到25.9T之巨!

  3 對於一個22.1G容量的表,B+樹的高度是3,如果要把非葉節點全部載入到記憶體也只需要少於18.8M的記憶體(如何得出的這個結論?因為對於高度為2的樹,1203個葉子節點也只需要18.8M空間,而22.1G從良表的高度是3,非葉節點1204個。節點儲存了行資料而非葉節點只有鍵和少量資料。

  2 Mysql的儲存引擎和索引

  可以說資料庫必須有索引,沒有索引則檢索過程變成了順序查找,O(n)的時間複雜度幾乎是不能忍受的。我們非常容易想像出一個只有單一關鍵字組成的表格如何使用B+樹進行索引,只要將關鍵字儲存到樹的節點即可。當資料庫一筆記錄包含多個字段時,一棵B+樹就只能儲存主鍵,如果檢索的是非主鍵字段,則主鍵索引失去作用,又變成順序查找了。這時應該在第二個要檢索的欄位上建立第二套索引。  這個索引由獨立的B+樹來組織。有兩種常見的方法可以解決多個B+樹存取相同套表資料的問題,一種稱為叢集索引(clustered index ),一種稱為非聚集索引(secondary index)。這兩個名字雖然都叫做索引,但這不是一種單獨的索引類型,而是一種資料儲存方式。對於叢集索引儲存來說,行資料和主鍵B+樹儲存在一起,輔助鍵B+樹只儲存輔助鍵和主鍵,主鍵和非主鍵B+樹幾乎是兩種類型的樹。對於非聚集索引儲存來說,主鍵B+樹在葉子節點儲存指向真正資料行的指針,而非主鍵。

  InnoDB使用的是聚集索引,將主鍵組織到一棵B+樹中,而行資料就儲存在葉子節點上,若使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索演算法即可找出對應的葉節點,之後再取得行資料。若對Name列進行條件搜索,則需要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點取得對應的主鍵。第二步驟使用主鍵在主索引B+樹種再執行一次B+樹檢索操作,最後到達葉子節點即可取得整行資料。

  MyISM使用的是非叢集索引,非叢集索引的兩棵B+樹看上去沒什麼不同,節點的結構完全一致只是儲存的內容不同而已,主鍵索引B+樹的節點儲存了主鍵,輔助鍵索引B+樹儲存了輔助鍵。表數據儲存在獨立的地方,這兩顆B+樹的葉子節點都使用一個位址指向真正的表數據,對於表數據來說,這兩個鍵沒有任何差異。由於索引樹是獨立的,透過輔助鍵檢索無需存取主鍵的索引樹。

  為了更形象說明這兩種索引的區別,我們假想一個表格如下圖儲存了4行資料。其中Id作為主索引,Name作為輔助索引。圖示清晰的顯示了聚集索引和非聚集索引的差異。

  我們專注於叢集索引,看上去叢集索引的效率明顯要低於非叢集索引,因為每次使用輔助索引檢索都要經過兩次B+樹查找,這不是多此一舉嗎?聚集索引的優點在哪裡?

  1 由於行數據和葉子節點存儲在一起,這樣主鍵和行數據是一起被載入內存的,找到葉子節點就可以立刻將行數據返回了,如果按照主鍵Id來組織數據,獲得數據更快。

  2 輔助索引使用主鍵作為"指針" 而不是使用地址值作為指針的好處是,減少了當出現行移動或數據頁分裂時輔助索引的維護工作,使用主鍵值作為指針會讓輔助索引佔用更多的空間,換來的好處是InnoDB在移動行時無須更新輔助索引中的這個"指標"。也就是說行的位置(實作中透過16K的Page來定位,後面會涉及)會隨著資料庫裡資料的修改而改變(前面的B+樹節點分裂以及Page的分裂),使用叢集索引就可以保證不管這個主鍵B+樹的節點如何變化,輔助索引樹都不受影響。

  3 Page結構

  如果說前面的內容偏向於解釋原理,那後面就開始涉及具體實現了。

  理解InnoDB的實作不得不提Page結構,Page是整個InnoDB儲存最基本的構件,也是InnoDB磁碟管理的最小單位,與資料庫相關的所有內容都儲存在這種Page結構裡。 Page分為幾種類型,常見的頁類型有資料頁(B-tree Node)Undo頁(Undo Log Page)系統頁(System Page) 交易資料頁(Transaction System Page)等。單一Page的大小是16K(編譯宏UNIV_PAGE_SIZE控制),每個Page使用一個32位元的int值來唯一標識,這也剛好對應InnoDB最大64TB的儲存容量(16Kib * 2^32 = 64Tib)。一個Page的基本架構如下圖所示:

#

  每個Page都有通用的頭和尾,但是中部的內容根據Page的類型不同而改變。 Page的頭部有我們關心的一些數據,下圖把Page的頭部詳細資料顯示出來:

  我們重點關注和數據組織結構相關的欄位:Page的頭部保存了兩個指針,分別指向前一個Page和後一個Page,頭部還有Page的類型資訊和用來唯一標識Page的編號。根據這兩個指標我們很容易想像出Page連結起來就是一個雙向鍊錶的結構。

  再看看Page的主體內容,我們主要關注行資料和索引的存儲,他們都位於Page的User Records部分,User Records佔據Page的大部分空間,User Records由一筆的Record組成,每筆記錄代表索引樹上的一個節點(非葉子節點和葉子節點)。在一個Page內部,單鍊錶的頭尾由固定內容的兩條記錄來表示,字串形式的"Infimum"代表開頭,"Supremum"代表結尾。這兩個用來代表開頭結尾的Record存放在System Records的段落裡,這個System Records和User Records是兩個平行的段落。 InnoDB存在4種不同的Record,它們分別是1主鍵索引樹非葉節點 2主鍵索引樹葉子節點 3輔助鍵索引樹非葉節點 4輔助鍵索引樹葉子節點。這4種節點的Record格式有一些差異,但是它們都儲存著Next指標指向下一個Record。後續我們會詳細介紹這4種節點,現在只要要把Record當成一個儲存了資料同時含有Next指標的單鍊錶節點即可。

  User Record在Page內以單鍊錶的形式存在,最初資料是按照插入的先後順序排列的,但是隨著新資料的插入和舊資料的刪除,資料物理順序會變得混亂,但他們依然保持著邏輯上的先後順序。

  把User Record的組織形式和若干Page組合起來,就看到了稍微完整的形式。

  現在看下如何定位一個Record:

  1 透過根節點開始遍歷一個索引的B+樹,透過各層非葉子節點最終到達一個Page,這個Page裡存放的都是葉子節點。

  2 在Page內從"Infimum"節點開始遍歷單鍊錶(這種遍歷往往會被優化),如果找到該鍵則成功返回。如果記錄到達了"supremum",表示當前Page裡沒有合適的鍵,這時要藉助Page的Next Page指針,跳到下一個Page繼續從"Infimum"開始逐個查找。

  詳細看下不同類型的Record裡到底儲存了什麼數據,根據B+樹節點的不同,User Record可以被分成四種格式,下圖種依照顏色予以區分。

  1 主索引樹非葉節點(綠色)

  1 子節點儲存的主鍵裡最小的值(Min Cluster Key on Child),這是B+樹必須的,作用是在一個Page裡定位到具體的記錄的位置。

  2 最小的值所在的Page的編號(Child Page Number),作用是定位Record。

  2 主索引樹葉子節點(黃色)

  1 主鍵(Cluster Key Fields),B+樹必須的,也是資料行的一部份

  2 除去主鍵以外的所有列(Non-Key Fields),這是資料行的除去主鍵的其他所有列的集合。

  這裡的1和2兩部分加起來就是一個完整的資料行。

  3 輔助索引樹非葉節點非(藍色)

  1 子節點裡儲存的輔助鍵值裡的最小的值(Min Secondary-Key on Child),這是B+樹必須的,作用是在一個Page裡定位到具體的記錄的位置。

  2 主鍵值(Cluster Key Fields),為什麼非葉子節點要儲存主鍵呢?因為輔助索引是可以不唯一的,但是B+樹要求鍵的值必須唯一,所以這裡把輔助鍵的值和主鍵的值合併起來作為在B+樹中的真正鍵值,保證了唯一性。但這也導致在輔助索引B+樹中非葉節點反而比葉子節點多了4個位元組。 (即下圖中藍色節點反而比紅色多了4位元組)

  3 最小的值所在的Page的編號(Child Page Number),作用是定位Record。

  4 輔助索引樹葉子節點(紅色)

  1 輔助索引鍵值(Secondary Key Fields),這是B+樹必須的。

  2 主鍵值(Cluster Key Fields),用來在主索引樹裡再做一次B+樹檢索來找到整個記錄。

  以下是本篇最重要的部分了,結合B+樹的結構和前面介紹的4種Record的內容,我們終於可以畫出一幅全景圖。由於輔助索引的B+樹與主鍵索引有相似的結構,這裡只畫出了主鍵索引樹的結構圖,只包含了"主鍵非葉節點"和"主鍵葉子節點"兩種節點,也就是上圖的的綠色和黃色的部分。

  把上圖還原成下面這個更簡潔的樹形示意圖,這就是B+樹的一部分。注意Page和B+樹節點之間並沒有一一對應的關係,Page只是作為一個Record的保存容器,它存在的目的是便於對磁碟空間進行批量管理,上圖中的編號為47的Page在樹形結構上就被拆分成了兩個獨立節點。

  至此本篇就算結束了,本篇只是對InnoDB索引相關的資料結構和實作進行了一些梳理總結,並未涉及到Mysql的實戰經驗。這主要是基於幾點原因:

  1 原理是基石,只有充分了解InnoDB索引的工作方式,我們才有能力高效的使用好它。

  2 原理性知識特別適合使用圖示,我個人非常喜歡這種表達方式。

  3 關於InnoDB優化,在《高性能Mysql》裡有更加全面的介紹,對優化Mysql感興趣的同學完全可以自己獲取相關知識,我自己的積累還未達到能分享這些內容的地步。

  另:對InnoDB實作有更多興趣的同學可以看看Jeremy Cole的部落格(參考文獻三篇文章的來源),這位老兄曾先後在Mysql,Yahoo,Twitter,Google從事資料庫相關工作,他的文章非常棒!

 以上就是MySQL的InnoDB索引原理詳解的內容,更多相關內容請關注PHP中文網(www.php.cn)! 


#
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
MySQL與Sqlite有何不同?MySQL與Sqlite有何不同?Apr 24, 2025 am 12:12 AM

MySQL和SQLite的主要區別在於設計理念和使用場景:1.MySQL適用於大型應用和企業級解決方案,支持高性能和高並發;2.SQLite適合移動應用和桌面軟件,輕量級且易於嵌入。

MySQL中的索引是什麼?它們如何提高性能?MySQL中的索引是什麼?它們如何提高性能?Apr 24, 2025 am 12:09 AM

MySQL中的索引是數據庫表中一列或多列的有序結構,用於加速數據檢索。 1)索引通過減少掃描數據量提升查詢速度。 2)B-Tree索引利用平衡樹結構,適合範圍查詢和排序。 3)創建索引使用CREATEINDEX語句,如CREATEINDEXidx_customer_idONorders(customer_id)。 4)複合索引可優化多列查詢,如CREATEINDEXidx_customer_orderONorders(customer_id,order_date)。 5)使用EXPLAIN分析查詢計劃,避

說明如何使用MySQL中的交易來確保數據一致性。說明如何使用MySQL中的交易來確保數據一致性。Apr 24, 2025 am 12:09 AM

在MySQL中使用事務可以確保數據一致性。 1)通過STARTTRANSACTION開始事務,執行SQL操作後用COMMIT提交或ROLLBACK回滾。 2)使用SAVEPOINT可以設置保存點,允許部分回滾。 3)性能優化建議包括縮短事務時間、避免大規模查詢和合理使用隔離級別。

在哪些情況下,您可以選擇PostgreSQL而不是MySQL?在哪些情況下,您可以選擇PostgreSQL而不是MySQL?Apr 24, 2025 am 12:07 AM

選擇PostgreSQL而非MySQL的場景包括:1)需要復雜查詢和高級SQL功能,2)要求嚴格的數據完整性和ACID遵從性,3)需要高級空間功能,4)處理大數據集時需要高性能。 PostgreSQL在這些方面表現出色,適合需要復雜數據處理和高數據完整性的項目。

如何保護MySQL數據庫?如何保護MySQL數據庫?Apr 24, 2025 am 12:04 AM

MySQL數據庫的安全可以通過以下措施實現:1.用戶權限管理:通過CREATEUSER和GRANT命令嚴格控制訪問權限。 2.加密傳輸:配置SSL/TLS確保數據傳輸安全。 3.數據庫備份和恢復:使用mysqldump或mysqlpump定期備份數據。 4.高級安全策略:使用防火牆限制訪問,並啟用審計日誌記錄操作。 5.性能優化與最佳實踐:通過索引和查詢優化以及定期維護兼顧安全和性能。

您可以使用哪些工具來監視MySQL性能?您可以使用哪些工具來監視MySQL性能?Apr 23, 2025 am 12:21 AM

如何有效監控MySQL性能?使用mysqladmin、SHOWGLOBALSTATUS、PerconaMonitoringandManagement(PMM)和MySQLEnterpriseMonitor等工具。 1.使用mysqladmin查看連接數。 2.用SHOWGLOBALSTATUS查看查詢數。 3.PMM提供詳細性能數據和圖形化界面。 4.MySQLEnterpriseMonitor提供豐富的監控功能和報警機制。

MySQL與SQL Server有何不同?MySQL與SQL Server有何不同?Apr 23, 2025 am 12:20 AM

MySQL和SQLServer的区别在于:1)MySQL是开源的,适用于Web和嵌入式系统,2)SQLServer是微软的商业产品,适用于企业级应用。两者在存储引擎、性能优化和应用场景上有显著差异,选择时需考虑项目规模和未来扩展性。

在哪些情況下,您可以選擇SQL Server而不是MySQL?在哪些情況下,您可以選擇SQL Server而不是MySQL?Apr 23, 2025 am 12:20 AM

在需要高可用性、高級安全性和良好集成性的企業級應用場景下,應選擇SQLServer而不是MySQL。 1)SQLServer提供企業級功能,如高可用性和高級安全性。 2)它與微軟生態系統如VisualStudio和PowerBI緊密集成。 3)SQLServer在性能優化方面表現出色,支持內存優化表和列存儲索引。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),