深入理解MySQL索引底層數據結構

来源:https://www.cnblogs.com/Jcloud/archive/2023/04/06/17291778.html
-Advertisement-
Play Games

在日常工作中,我們會遇見一些慢SQL,在分析這些慢SQL時,我們通常會看下SQL的執行計劃,驗證SQL執行過程中有沒有走索引。通常我們會調整一些查詢條件,增加必要的索引,SQL執行效率就會提升幾個數量級。我們有沒有思考過,為什麼加了索引就會能提高SQL的查詢效率,為什麼有時候加了索引SQL執行反而會... ...


1 引言

在日常工作中,我們會遇見一些慢SQL,在分析這些慢SQL時,我們通常會看下SQL的執行計劃,驗證SQL執行過程中有沒有走索引。通常我們會調整一些查詢條件,增加必要的索引,SQL執行效率就會提升幾個數量級。我們有沒有思考過,為什麼加了索引就會能提高SQL的查詢效率,為什麼有時候加了索引SQL執行反而會沒有變化,本文就從MySQL索引的底層數據結構和演算法來進行詳細分析。

2 索引數據結構對比

索引的定義:索引(Index)是幫助MySQL高效獲取數據的排好序的數據結構。

索引中常見的數據結構有以下幾種:

  • Hash表
  • 二叉樹
  • 紅黑樹
  • B-Tree
  • B+Tree

Hash表
通過索引的key進行一次hash計算,就可以快速獲取磁碟文件指針,對於指定索引查找文件非常快,但是對於範圍查找沒法支持,有時候也會出現Hash衝突的情況。

二叉樹
二叉樹的特點:左邊子節點的數據小於父節點數據,右邊子節點的數據大於父節點數據。如下圖所示,如果col2是索引,查找索引為65的行元素,只需要查找兩次,就可以獲取到行元素所在的磁碟指針地址。

但如果是一個按照順序遞增的值,例如為col1建立索引,不再適合使用二叉樹建立索引,因為此時使用二叉樹建立索引將會變成一個鏈式索引,此時的索引結構如下圖所示,如果查找6節點需要6次遍歷才能找到。

紅黑樹
紅黑樹是一種二叉平衡樹,可以提高查詢效率,此時若再查找6節點只需要遍歷3次就能找到了。但紅黑樹也有缺點,當存儲大數據量時,樹的高度就會變的不可控, 數量越大,樹的高度越高,查詢的效率將會大大降低。

B-Tree
B-Tree是一種多路二叉樹,所具有的特點:1 葉節點具有相同的深度,葉節點的指針為空;2 所有索引元素不重覆;3 節點中的數據索引從左到右遞增排列。

B+Tree
B+Tree是B-Tree的變種,所具有的特點:1 非葉子節點不存儲data,只存儲索引(冗餘),可以放更多的索引;2 葉子節點包含所有索引欄位;3 葉子節點用指針連接,提高區間訪問的性能。

與紅黑樹相比,B-Tree和B+Tree兩種數據結構都更加矮胖,存儲相同數量級的索引數據時,層級更低。

B-Tree和B+Tree之間一個很大的不同,是B+Tree的節點上不儲存value,只儲存key,而葉子節點上儲存了所有key-value集合,並且節點之間都是有序的。這樣的好處是每一次磁碟IO能夠讀取的節點更多,也就是樹的度(Max.Degree)可以設置的更大一些,因為每次磁碟IO讀取的磁碟頁數是一定的。例如,每次磁碟IO能夠讀取1頁=4kb,那麼省去value的情況下同樣一頁數據能夠讀取更多的key,這樣就大大減少了磁碟的IO次數。

此外,B+Tree也是排好序的數據結構,資料庫中><或者order by等都可以直接依賴這一特性。

MySQL中對於索引使用的主要數據結構也是B+Tree,目的也是在讀取數據時能夠減少磁碟IO。

3 千萬級數據如何用B+樹索引快速查找

MySQL 官方對非葉子節點(如最上層 h = 1的節點,B+Tree高度為3) 的大小是有限制的,最大的大小是16K,可以通過以下SQL語句查詢到,當然這個值是可以調的,既然官方給出這個閾值說明再大的話會影響磁碟IO效率。

從執行結果,可以看到大小為 16384,即 16K大小。

假如:B+Tree的表都存滿了。主鍵索引的類型為BigInt,大小為8B,指針存儲了下個節點的文件地址,大小為6B。最後一層,假如 存放的數據data為1K 大小,那麼

  1. 第一層最大節點數為: 16k / (8B + 6B) ≈ 1170 (個);
  2. 第二層最大節點數也應為:1170個;
  3. 第三層最大節點數為:16K / 1K = 16 (個)。

則,一張B+Tree的表最多存放 1170_1170_16 ≈ 2千萬。

所以,通過分析,我們可以得出,B+Tree結構的表可以容納千萬數據量的查詢。而且一般來說,MySQL會把 B+Tree 根節點放在記憶體中,那隻需要兩次磁碟IO就行。

4 存儲引擎索引實現

MySQL中索引儲存在哪裡呢?和數據一樣,索引以文件形式儲存在硬碟上。
在MyISAM儲存引擎中,數據和索引文件試試分開儲存的,數據存在.MYD結尾的文件中,索引單獨存在.MYI結尾的文件中。

在InnoDB中,數據和索引文件是合起來儲存的,註意下圖中沒有了.MYI結尾的文件,只有一個.ibd結尾的文件。

MyISAM索引文件和數據文件是分離的(非聚集),並且主鍵索引和輔助索引(二級索引)的儲存方式是一樣的。

InnoDB中索引文件和數據文件是同一個文件(聚集),並且主鍵索引和二級索引儲存方式有所不同,如圖所示,二級索引的葉子節點不儲存數據,僅儲存主鍵ID。

這裡思考幾個問題:

  • 為什麼建議InnoDB表必須建主鍵,並且推薦使用整型的自增主鍵?
  • 為什麼非主鍵索引結構葉子節點存儲的是主鍵值?

如果我們在創建表時不設置主鍵,InnoDB會自動幫我們從第一列開始篩選一列數據不重覆的列做為主鍵,如果找不到這樣的列,就會創建一個隱藏的列(rowid)做為主鍵,這會增加很多MySQL的工作,所以建議我們在創建InnoDB表時一定要設置主鍵。

整型的欄位做為主鍵,一方面在數據比較時不需要進行轉換,另一方面存儲也比較節省空間。那為什麼要強調主鍵自增呢?如果主鍵id是無序的,那麼很有可能新插入的值會導致當前節點分裂,此時MySQL不得不為了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁碟上而從緩存中清掉,此時又要從磁碟上讀回來,這增加了很多開銷,同時頻繁的移動、分頁操作造成了大量的碎片,得到了不夠緊湊的索引結構,後續不得不通過OPTIMIZE TABLE來重建表並優化填充頁面。反之,如果每次插入有序,那就會在當前頁後面連續寫入,寫不下就會重新分配一個節點,記憶體都是連續的,這樣效率自然也就最高了。

非主鍵索引的葉子節點存儲主鍵值而非全部數據,主要也是為了一致性和節省空間。如果二級索引儲存的也是數據,那麼每次插入MySQL都不得不更新每棵索引樹,這樣就加劇了新增編輯時的性能損耗,並且這樣一來空間利用率也不高,必然產生了大量冗餘數據。

5 聯合索引底層數據結構又是怎樣的

聯合索引又叫複合索引,例如下表:

CREATE TABLE `test` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL,
`age` int NOT NULL,
`position` varchar(32) NOT NULL,
`address` varchar(128) NOT NULL,
`birthday` date NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

如下索引就是一個聯合索引。

`idx_name_age_position` (`name`,`age`,`position`) USING BTREE

聯合索引底層數據結構長什麼樣?

比較相等時,先比較第一列的值,如果相等,再繼續比較第二列,以此類推。

瞭解了聯合索引的存儲結構,我們就知道了索引最左首碼優化原則是怎麼回事了,在使用聯合索引時,對於索引列的定義順序將會影響到最終查詢時索引的使用情況。例如聯合索引(name,age,position),MySQL會從最左邊的列優先匹配,如果最左邊的帶頭大哥name沒有使用到,在未使用覆蓋索引的情況下,就只能全表掃描。

聯合底層數據結構思考:MySQL會優先以聯合索引第一列匹配,此後才會匹配下一列,如果不指定第一列匹配的值,也就無法得知下一步查詢哪個節點。

6 總結

索引本質上是一種排好序的數據結構,瞭解了MySQL索引的底層數據結構及存儲原理,可以幫助我們更好地進行SQL優化。其實資料庫索引調優是一項技術活,不能僅僅靠理論,因為實際情況千變萬化,而且MySQL本身存在很複雜的機制,如查詢優化策略和各種引擎的實現差異等都會使情況變得更加複雜。但同時這些理論是索引調優的基礎,只有在明白理論的基礎上,才能對調優策略進行合理推斷並瞭解其背後的機制,然後結合實踐中不斷的實驗和摸索,從而真正達到高效使用MySQL索引的目的。

最後,如果大家想再溫習一下數據結構的知識,這個數據結構網站(https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)不可錯過,可以很好地幫助我們演示數據結構的存儲過程。

作者:京東物流 於朔


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

-Advertisement-
Play Games
更多相關文章
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是利用i.MXRT1xxx系列ROM集成的DCD功能可輕鬆配置指定外設。 關於 i.MXRT1xxx 系列晶元 BootROM 中集成的 DCD 功能這個話題,痞子衡早就想寫了,但是一直沒有動筆,畢竟這個話題比較生澀,單獨講會比較枯燥。最 ...
  • 在安裝Mac電腦應用程式的時候,經常會遇到“xxx.app已損壞,打不開。您應該將它移到廢紙簍“或”打不開的xxx.app,因為它來自身份不明的開發者”,如圖: 遇到上述情況是不是真的要移動到廢紙簍呢?下麵小編就為您帶來Mac應用程式無法打開提示不明開發者或文件損壞的處理方法,解答Mac應用程式無法 ...
  • 作者:袁首京 原創文章,轉載時請保留此聲明,並給出原文連接。 技術人員多數又呆板又花心不長久。我知道你可能已經厭倦了 Docker,但是系統還沒有複雜到需要高攀 K8S 的地步。那我建議您,有空的話可以約一下 Podman。 Podman 使用起來是足夠簡單的,直接把它當做改了名字的 Docker ...
  • 1. HAVING子句的用法 1.1. 學習SQL時最大的阻礙就是我們已經習慣了的面向過程語言的思考方式(排序、迴圈、條件分支、賦值等) 1.2. 只有習慣了面向集合的思考方式,才能真正地學好它 1.3. 幫助我們順利地忘掉面向過程語言的思考方式並理解SQL面向集合特性的最為有效的方法 1.4. H ...
  • # 大數據開發基礎學習編程語言往往是我們開啟學習之路的第一大步。大數據領域的很多框架都是基於Java語言開發的,而且各種框架也都提供了Java API來提供使用和操作介面,所以Java語言的學習逃不掉。除此之外Scala在必要時也可以學一下,在大數據開發領域里用得還是挺多的。Scala語言的表達能力 ...
  • 數據管理技術的發展 第一節 資料庫技術發展概述 數據模型是資料庫系統的核心和基礎 以數據模型的發展為主線,資料庫技術可以相應地分為三個發展階段: 第一代的網狀、層次資料庫系統 第二代的關係資料庫系統 新一代的資料庫系統 一、第一代資料庫系統 層次資料庫系統 層次模型 網狀資料庫系統 網狀模型 層次模 ...
  • ORACLE資料庫中ORACLE_SID與INSTANCE_NAME在概念和意義上有什麼異同呢?下麵簡單來總結概況一下,很多時候,不少人都搞不清楚兩者的異同,甚至認為兩者是等價的。 ORACLE_SID與INSTANCE_NAME的異同 ORACLE_SID參數是操作系統的環境變數,用於和操作系統進 ...
  • 最近寫了幾個簡單的spark structured streaming 的代碼實例。 目的是熟悉spark 開發環境搭建, spark 代碼開發流程。 開發環境: 系統:win 11 java : 1.8 scala:2.13 工具:idea 2022.2 ,maven 3, git 2.37 sp ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...