MySQL深度理解-深入理解MySQL索引底層數據結構與算法

1.引言

????????在項目中會遇到各種各樣的慢查詢的問題,對于千萬級的表,如果使用比較笨的查詢方式,查詢一條SQL可能需要幾秒甚至幾十秒,如果將索引設置的比較合理,可以將查詢變得仍然非常快。

2.索引的本質

????????索引:幫助MySQL高效獲取數據的排好序的數據結構。

????????索引的數據結構:

????????1.二叉樹

????????2.紅黑樹

????????3.Hash表

????????4.B-Tree

????????如果表中沒有索引,檢索數據的時候就需要一行一行的去找,消耗的時間非常大。

????????每個數據在存儲的時候,存儲在磁盤上是,看起來是挨在一起的,但是其實不是挨在一起的,這個需要注意!!!

????????從磁盤中讀取一次數據,被稱之為磁盤IO,一次磁盤IO的性能是比較低的,如果我們對磁盤上的某一個數據進行查詢的時候,順序從表中查詢數據,每次拿一個進行比對一次,這樣整體來看是比較消耗性能的,整體的性能是不高的。

????????我們可以通過建立索引,將查詢(磁盤IO)的次數控制在一定量內,就可以大大提升性能了。

????????為什么不選擇使用搜索二叉樹?

????????搜索二叉樹并沒有自平衡的功能,很有可能退化為鏈表結構。

????????為什么不選擇使用平衡搜索二叉樹?如紅黑樹,AVL樹?

????????在平時生產環境使用數據庫的時候,單表數據量常常會達到百萬級,千萬級,假設單表數據量為500萬,即使使用紅黑樹,樹的高度也會達到22,那么即使使用索引查詢數據,也要可能進行22次磁盤IO操作,這個性能消耗對于MySQL來說也是比較大的。

????????為什么B-Tree更加合適?

????????1.B-Tree的特點:

????????葉節點具有相同的深度,葉節點的指針為空。

????????所有索引元素不重復。

????????節點中的數據索引從左到右遞增排列。

????????2.B-Tree的數據結構:

????????每個節點都會存儲多個key-value結構,key存儲的是索引值,data存儲的是索引所在行數據的硬盤地址。

????????為什么MySQL索引最終決定選擇使用B+Tree(B-Tree的變種)?

????????1.B+Tree的特點:

????????非葉子節點不存粗data,只存儲索引(做一個數據冗余),可以存放更多的索引數據。

????????葉子節點包含所有的索引字段。

????????葉子節點用指針連接,提高區間訪問的性能。

????????2.B+Tree的數據結構:

????????MySQL中默認設定的一個節點可以存儲的索引數據是16kb,可以使用SHOW GLOBAL STATUS like 'Innodb_page_size'查看“:

SHOW GLOBAL STATUS like 'Innodb_page_size';

????????這是MySQL設置的默認值,不建議去修改,這是MySQL官方進行了大量測試得出的結果,多方考量了各種情況。

????????現在舉例展示定位數據的整體過程:

????????整個查詢30這個索引對應的數據的過程花費的消耗是三次磁盤IO和一次內存IO,相對來說內存的速度是極快的,對于磁盤IO消耗的時間是可以忽略不計的,所以這次查詢消耗也就是三次網絡IO。

????????那為什么MySQL不直接干脆在一個節點上存儲所有索引呢?這樣只需要一次磁盤IO呀。

????????答案:在系統中,內存是有限的,生產環境下一個表存儲的數據可能會達到百萬級,千萬級,一次磁盤IO將所有的索引數據和數據查詢到內存中,數據量相對來說是巨大的,直接將內存干爆了,設計的不合理。而且這樣不就又變成了順序查找數據了嗎?這樣整體查詢數據的速度也不會很快的。

????????MySQL選擇16kb作為節點的存儲空間,可以存儲多少數據呢?

????????答案:這里以索引是bigint類型的作例子,bigint = 8byte,數據地址在MySQL底層C語言中是占用了6byte,16kb / (8 + 6)byte,一個節點即可以存儲1170個元素。假設data的數據是1kb,一個葉節點也是可以存儲16個元素的。

????????使用B+Tree對樹高度進行削減的效果?

????????假設表中有2000萬的數據,使用索引建立的B+Tree的高度也只有3而已,也就是說最多三次磁盤IO就可以找到相對應的數據了。

????????MySQL對于索引B+Tree節點常駐內存的操作。

????????在MySQL低版本中,會將根節點常駐到內存中,在MySQL高版本中,會將所有的非葉子節點進行常駐內存,也就是說對于千萬級數據量的表來說,查詢數據的時間成本消耗只有查詢葉節點中數據這一次的磁盤IO而已,可見查詢速度是相當快的。

????????面試題:對于B-Tree和B+Tree,為什么MySQL最終決定選擇使用B+Tree?

????????影響樹查詢的關鍵是什么?是樹的高度,樹越高需要進行的磁盤IO的次數就越多,2000萬級別的數據表的索引使用B+Tree進行構建出來的樹的高度僅有3,但是如果使用的是B-Tree,由于B-Tree的特性,構建起來整個樹的高度絕對是大于3的,而且也不能做內存常駐等優化手段。

3.不同存儲引擎索引實現

????????首先先明確一個點:存儲引擎的概念相對的是數據表,并不是相對的數據庫,一個數據庫中的多個數據表是可以分別使用不同的存儲引擎的。

3.1MylSAM存儲引擎

????????MySQL數據庫中的數據都是存儲在磁盤中的,如果數據表采用的是MyISAM存儲引擎,數據表在硬盤中對應存儲的是以下三個文件:

????????1.數據表名稱.frm:數據表結構存儲文件。

????????2.數據表名稱.MYD:數據表數據存儲文件。

????????3.數據表名稱.MYI:數據表索引存儲文件。

????????即MyISAM存儲引擎,數據存儲和索引存儲文件是分離存儲的。

????????在MYI索引文件中,存儲的其實就是數據表構建的這一棵B+樹,葉節點中的data數據是磁盤數據的地址,即當數據表采用的是MyISAM存儲引擎的時候,通過索引去查詢數據的時候,會根據B+Tree查詢到數據的地址,然后再去MYD文件中查詢真正的數據。

3.2InnoDB存儲引擎

????????如果數據表采用的InnoDB存儲引擎,數據表在硬盤中對應存儲的是以下兩個文件:

????????1.數據表名稱.frm:數據表結構存儲文件。

????????2.數據表名稱.ibd:數據表索引+數據存儲文件。

????????即InnoDB存儲引擎,數據存儲和索引存儲文件是聚焦存儲的。

????????在InnoDB存儲引擎中,通過索引字段查詢到的Data是數據表中的數據,即數據表中的其他數據是和索引字段存儲在一起的,即存儲在B+Tree的葉節點中。

3.3聚集索引和非聚集索引

????????非聚集索引:MyISAM存儲引擎本身建立的索引就是非聚集索引,B+Tree葉節點的索引對應的Data是數據的地址,不是數據本身,即葉節點索引和數據是分開的,便是非聚集索引。

????????聚集索引:InnoDB存儲引擎本身建立的索引就是聚集索引,B+Tree葉節點的索引對應的Data是數據本身,即葉節點索引和數據是黏合在一起的,便是聚集索引。

????????面試題:為什么建議InnoDB表必須建立主鍵,并且推薦使用整型的自增主鍵。

3.4面試題探究

3.4.1為什么InnoDB必須建立主鍵?

????????之所以InnoDB必須建立主鍵,這與以InnoDB為存儲引擎的數據表密切相關,InnoDB引擎的數據表中數據和索引是附著在一起的,所以數據表中一定必須要有一個索引,一般情況下,我們都是使用主鍵作為索引進行搭建B+Tree數據存儲結構。

????????如果發現數據表有主鍵,就會使用主鍵索引結合數據建立B+Tree結構存儲相關數據。

????????如果數據表沒有主鍵,就會去數據表中從前向后去尋找一個所有數據都不重復的一列數據,借助該列數據作為索引去構建B+Tree。

????????如果數據表沒有主鍵,且沒有從數據表中找到任何一列數據不一樣,那么MySQL就會單獨創建一個隱形的一列,去作為索引維護B+Tree。

????????雖然MySQL最后還是可以幫助我們建立起主鍵索引,但是不建議將這種工作要求給MySQL去完成。

3.4.2為什么索引建議使用整形數據列

????????前面提到過索引使用數據列中的數據建立的,并且每一層的整體元素都是以索引數據遞增建立的索引節點,假設索引列中數據是整形,那么索引節點之間進行比較會非常簡單,很容易就能比較成功,將整棵B+Tree建立起來。

????????但是如果索引數據不是整形數據,而是字符串數據之類的,比較大小就會比較耗費性能,整形數字容易比較,性能較高,所以建議索引建立走整形數據列。

3.4.3為什么索引建立使用自增整形數據列

????????前面已經介紹了為什么建議使用整形數據列,主要的目的就是為了提升索引樹構建時的比較性能。

????????其實索引元素選擇為自增的元素也是為了提高索引樹構建的整體性能。

????????設定B+Tree中單節點設定為最多可以有四個元素,現在B+Tree中有七個元素,分別是1,2,3,4,5,6,8,那么建立起來的B+Tree就會如下所示:

????????但是如果現在不遵循自增原則,現在插入一個0007元素進去,會將元素插入到0006和0007之間:

????????但是現在最右側的節點已經達到了最大元素數量,所以會進行分裂為兩個節點,分裂為兩個節點之后就會如下所示:

????????可以發現除了進行節點分裂操作以外,還進行了自平衡操作,整個過程的時間成本損耗是比較大的,所以建議建立索引的時候是整形自增的。

4.聚簇索引和非聚簇索引

????????在MySQL中索引被劃分為兩種,一種是聚簇索引另一種是非聚簇索引。

????????其中聚簇索引的意思是索引葉節點對應的數據是原始數據(即真數據),非聚簇索引的意思是索引葉節點對應的數據是數據的地址。

????????接下來就從存儲引擎和索引類型兩個方面去區分講解。

4.1存儲引擎

????????MyISAM存儲引擎使用的索引是非聚簇索引,即索引B+Tree的葉節點對應的數據是數據的地址,不是真實的數據。

????????InnoDB存儲引擎使用的一級索引(即主鍵索引)是聚簇索引,二級索引是非聚簇索引,下一步詳細講一下為什么要如此劃分。

4.2索引類型

????????這里主要是介紹InnoDB存儲引擎的索引類型。

????????索引類型分為主鍵索引和二級索引,主鍵索引顧名思義就是該字段既是主鍵又是索引,二級索引就是主鍵之外的字段建立的索引。

????????主鍵索引的B+Tree索引樹葉節點對應的數據是真實數據,所以主鍵索引被稱之為聚簇索引。

????????二級索引的B+Tree索引樹葉節點對應的數據是數據的地址,所以二級索引被稱之為非聚簇索引,二級索引之所以被設計為是非聚簇索引是因為為了達到節省空間的效果。

????????二級索引都需要進行一次回表操作,通過回表操作查詢真實的數據。

5.Hash索引

????????InnoDB存儲引擎的數據表,除了可以建立以B+Tree為存儲數據結構的索引以外,還可以建立起以Hash為存儲結構的索引。

????????Hash索引不支持范圍查找但是如果只是進行等值/IN查找速度會非常快,可以在項目中靈活使用(IO查詢次數邵),因為對索引的key進行一次查詢就可以定位到數據。

????????Hash索引的索引對應的數據都是數據的地址,屬于非聚簇索引。

6.聯合索引(多字段索引)

6.1概念

????????KEY idx_name_age_position (name age position) USING BTREE

????????這條語句的意思是建立一個使用B+Tree結構構建的聯合索引idx_name_age_position,該聯合索引涉及到多個字段,分別是name,age,position字段。

????????構建出的B+Tree如下:

????????其中數據進行排序的時候,是先按照name排序,再按照age排序,再按照position進行排序。

????????如果聯合索引建立的是聯合主鍵索引,葉節點對應的數據就是主鍵索引,通過聯合索引查詢到數據后,就會先獲取到主鍵索引數據,再去回表查詢主鍵索引對應的真實數據。

????????如果是輔助索引,

6.2最左前綴原則

????????最左前綴原則保證了,聯合索引只在SELECT查詢語句使用時,有最左字段時才會生效使用,否則索引不會生效。

????????解釋一下為什么不會生效,還是看剛剛建立的索引樹:

????????查詢語句1:SELECT * FROM table WHERE name = Bill AND age = 30

????????執行該SQL語句時,會先通過name定位到節點,然后查詢到最左邊的數據時,發現該數據是最后一個30,又由于數據組織是排好序的,所以就不會繼續向下找了,該索引樹起到了輔助快速查詢的作用。

????????查詢語句2:SELECT * FROM table WHERE age = 30。

????????如果是這樣就無法進行正常定位了,因為無法age只能在確定的name里是小范圍有序的,如果放到全局來看,age是亂序的,所以此時聯合索引會失效,就不會走聯合索引了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92521.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92521.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92521.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Django母嬰商城項目實踐(九)- 商品列表頁模塊

9、商品列表頁模塊 1、業務邏輯 商品模塊分為:商品列表頁 和 商品詳情頁 商品列表頁將所有商品按照一定的規則排序展示,用于可以從銷量、價格、上架時間和收藏數量設置商品的排序方式,并且在商品左側設置分類列表,選擇某一個分類可以篩選出對應的商品信息。 商品列表頁…

8、STM32每個系列的區別

1、F1和F4的系列的區別 F1采用Crotex M3內核,F4采用Crotex M4內核。F4比F1的主頻高。F4具有浮點數運算單元,F1沒有浮點單元。F4的具備增強的DSP指令集。F407的執行16位DSP指令的時間只有F1的30%~70%。F4執行32位DSP指令的時間只有F1的25% ~ 60%。F1內部S…

DeepSPV:一種從2D超聲圖像中估算3D脾臟體積的深度學習流程|文獻速遞-醫學影像算法文獻分享

Title題目DeepSPV: A deep learning pipeline for 3D spleen volume estimation from 2Dultrasound imagesDeepSPV:一種從2D超聲圖像中估算3D脾臟體積的深度學習流程01文獻速遞介紹1.1 臨床背景 脾腫大指脾臟增大,是多種潛在疾病的重要臨床指標&#x…

病歷數智化3分鐘:AI重構醫院數據價值鏈

一、方案概述本方案針對某省醫聯體醫院病例數據管理需求,通過AI技術實現病歷數字化→信息結構化→數據應用化的全流程改造。系統采用雙端協同架構: - 普通用戶端:為一線醫護人員提供病歷拍攝、AI識別修正、安全上傳功能 - 管理員后臺&#…

CSS+JavaScript 禁用瀏覽器復制功能的幾種方法

🛡? 禁用瀏覽器復制功能完整指南 網頁中禁用用戶的復制功能,包括 CSS 方法、JavaScript 方法、綜合解決方案以及實際應用場景。適用于需要保護內容版權、防止惡意爬取或提升用戶體驗的場景。 📋 目錄 🚀 快速開始&#x1f3a8…

Java 虛擬線程在高并發微服務中的實戰經驗分享

Java 虛擬線程在高并發微服務中的實戰經驗分享 虛擬線程(Virtual Threads)作為Java 19引入的預覽特性,為我們在高并發微服務場景下提供了一種更輕量、易用的并發模型。本文結合真實生產環境,講述在Spring Boot微服務中引入和使用虛…

《拆解WebRTC:NAT穿透的探測邏輯與中繼方案》

WebRTC以其無需插件的便捷性,成為連接全球用戶的隱形橋梁。但很少有人知曉,每一次流暢的視頻對話背后,都藏著一場與網絡邊界的無聲博弈——NAT,這個為緩解IPv4地址枯竭而生的技術,既是網絡安全的屏障,也是端…

前端開發 React 組件優化

1. 使用 React.memo 進行組件優化問題:當父組件重新渲染時,子組件也會重新渲染,即使它的 props 沒有變化。解決方案:使用 React.memo 包裹子組件,讓其只在 props 變化時才重新渲染。示例場景:展示一個顯示計…

變頻器實習DAY12

目錄變頻器實習DAY12一、繼續,柔性平臺測試!上午 王工Modbus新功能測試下午 柔性平臺繼續按照說明書再測一遍附加的小知識點中國貍花貓.git文件附學習參考網址歡迎大家有問題評論交流 (* ^ ω ^)變頻器實習DAY12 一、繼續,柔性平臺測試&…

Redis--多路復用

🧩 一、什么是“客戶端連接”?所謂 客戶端連接 Redis,指的是:一個程序(客戶端)通過網絡連接到 Redis 服務端(比如 127.0.0.1:6379),建立一個 TCP 連接,雙方可…

數組——初識數據結構

一維數組數組的創建數組是一種相同類型元素的集合數組的創建方式C99 中引入了變長數組的概念,變長數組支持數組的大小使用變量來指定明顯這里的vs2019不支持變長數組數組初始化和不完全初始化第二個數組就是典型的不完全初始化,開辟了10個空間&#xff0…

技術速遞|使用 Semantic Kernel 與 A2A 協議構建多智能體解決方案

作者:盧建暉 - 微軟高級云技術布道師 翻譯/排版:Alan Wang 在快速發展的 AI 應用開發領域,能夠協調多個智能體已成為構建復雜企業級解決方案的關鍵。雖然單個 AI 智能體擅長特定任務,但復雜的業務場景往往需要跨平臺、跨框架甚至跨…

前端跨域請求原理及實踐

在前端開發中,"跨域"是一個繞不開的話題。當我們的頁面嘗試從一個域名請求另一個域名的資源時,瀏覽器往往會拋出類似Access to fetch at xxx from origin xxx has been blocked by CORS policy的錯誤。下面將深入探討跨域請求的底層原理&#…

SpringBoot07-數據層的解決方案:SQL

一、內置數據源 1-1、【回顧】Druid數據源的配置 druid的兩種導入格式 1-2、springboot提供的3種內置數據源的配置 若是不配置Druid, springboot提供了3中默認的數據源配置,它們分別是: 1. HikariCP(默認) 從 Spring…

前端自動化埋點:頁面模塊級行為跟蹤與問題定位系統??的技術設計方案

一、核心設計目標??精細化監控??:定位到頁面中??單個模塊??的曝光、點擊等行為。??低侵入性??:業務代碼與埋點邏輯解耦,降低開發維護成本。??鏈路可追蹤??:串聯用戶從曝光到操作的完整行為路徑。??實時性??&a…

Node.js 與 Java 性能對比

一、核心架構與任務模型對比Node.js 單線程事件循環 非阻塞I/O 通過V8引擎執行JavaScript,采用事件驅動模型,所有I/O操作(如網絡請求、文件讀寫)均為非阻塞。單線程處理所有請求,但通過事件循環(Event Loo…

Python3常見接口函數

Python3常見接口函數一、基礎內置函數 輸入輸出 print():輸出內容input():讀取用戶輸入 類型轉換 int()、float()、str()、bool():基礎類型轉換list()、tuple()、set()、dict():容器類型轉換bin()、hex()、oct():進制轉…

《P4092 [HEOI2016/TJOI2016] 樹》

題目描述在 2016 年,佳媛姐姐剛剛學習了樹,非常開心。現在他想解決這樣一個問題:給定一顆有根樹,根為 1 ,有以下兩種操作:標記操作:對某個結點打上標記。(在最開始,只有結…

TCP頭部

TCP頭部字段詳解1. 源端口和目的端口(各16位)功能:標識發送和接收應用程序范圍:0-65535(0-1023為知名端口)技術細節:客戶端通常使用臨時端口(1024-65535)服務端使用固定端…

LinkedList與鏈表(單向)(Java實現)

引入鏈表結構:在ArrayList任意位置插入或者刪除元素時,就需要將后序元素整體往前或者往后 搬移,時間復雜度為O(n),效率比較低,因此ArrayList不適合做任意位置插入和刪除比較多的場景。因此:java集合中又引入…