搞懂MySQL數據庫索引數據結構這一篇足夠從此不再萌萌噠

點贊多大膽,就有多大產!開源促使進步,獻給每一位技術使用者和愛好者!
乾貨滿滿,擺好姿勢,點贊發車

前言

  說到數據庫優化脫口而出就是添加索引,如果不會用請移步《解鎖數據庫系列》數據庫索引已爲你備好!如果你也和我一樣一直搞不懂數據庫索引底層數據結構,懵X樹上懵X果,懵X樹下你和我,請在下方留言告訴我不止我一個,心裏好受點,可是現在我已悟透,看完整篇文章相信你也和我一樣撥開迷霧對天笑說:就這?對它就這,話不多說直接發車!

索引數據結構

  數據庫索引主要有Hash表、二叉樹、紅黑樹、B樹、B+樹,我們MySQL使用的是B+樹!

Hash索引

介紹

  Hash索引(hash index)基於哈希表實現,對於每一行數據,存儲引擎都會對所有的索引列計算一個hash碼(hash code),哈希碼是一個較小的值,哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。如果hash碼一樣則會採用鏈表的形式存儲,類似於HashMap,Hash索引適用於精準查詢。

舉例

  有下表
在這裏插入圖片描述
  如果我們在name列建立索引,name數據庫會使用哈希算法計算name列每一行數據的hash值並進行存儲。因爲Hash值是隨機計算的,所以可能存在衝突,假如計算結果如下
在這裏插入圖片描述
  Hash索引數據結構如下
在這裏插入圖片描述

我們有一條SELECT id,name,age FROM t_user WHERE name=‘石小添’; 這樣的一條SQL可以直接對石小添 按哈希算法算出來一個Hash值,通過該值找到對應的記錄指針,通過記錄指針找到表中的哪一行數據,最後比較name是否爲石小添,以保證就是要查找的行。
但是如果我們有 SELECT id,name,age FROM t_user WHERE name>‘石小添’; 這樣的一條SQL則無能爲力,因爲Hash表支持快速的精確查詢,但是不支持範圍查詢

Hash索引總結

  • 哈希索引只包含哈希值和行指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行
  • 哈希索引數據並不是按照索引值順序存儲的,所以也就無法用於排序
  • 哈希索引不支持部分索引列匹配查找,因爲哈希索引始終是使用索引列的全部內容來計算哈希值
  • 哈希索引只支持等值比較查詢,不支持任何範圍查詢
  • 訪問哈希索引的數據非常快,除非有很多哈希衝突(不同的索引列值卻有相同的哈希值)。當出現哈希衝突的時候,存儲引擎必須遍歷鏈表中所有的行指針,逐行進行比較,直到找到所有符合條件的行
  • 如果哈希衝突很多的話,一些索引維護操作的代價也會很高。例如,如果在某個選擇性很低(哈希衝突很多)的列上建立哈希索引,那麼當從表中刪除一行時,存儲引擎需要遍歷對應哈希值的鏈表中的每一行,找到並刪除對應行的引用,衝突越多,代價越大

二叉樹

  二叉樹(Binary Tree)是每個結點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查找樹和二叉堆。
在這裏插入圖片描述

舉例

在這裏插入圖片描述

id列添加索引使用二叉樹進行存儲,如下圖

在這裏插入圖片描述

如果我們的數據是單邊增長的最終二叉樹可能成爲一個鏈表,我們查詢一個數據如下圖

在這裏插入圖片描述

如果有一條SQLSELECT id,name,age FROM tb_user WHERE id=7 ,對該字段創建索引並且使用的是二叉樹維護它會查找6次,速度和沒有創建索引是一樣的!
二叉樹索引在索引字段是連續時或其他場景下性能很低,而且這棵樹是不平衡的嚴重傾斜,我們就此引出紅黑樹

二叉樹特點

  • 若它的左子樹不空,則左子樹上所有結點的值均小於它的根節點的值;
  • 若它的右子樹上所有結點的值均大於它的根節點的值;
  • 它的左、右子樹也分別爲二叉排序樹

紅黑樹

  紅黑樹(Red Black Tree)是一種含有紅黑結點並能自平衡的二叉查找樹,是一種平衡二叉樹。紅黑樹的每個節點上都有存儲表示節點的顏色,可以是紅(Red)或黑(Black)

相同的,id列添加索引使用紅黑樹進行存儲,如下圖,我們會發現會做一個調整,使這棵樹相對平衡,較小的值放上級節點左邊,較大的值放上級節點右邊

在這裏插入圖片描述

相同的去查找一樣的4和7兩個數據,如下圖

在這裏插入圖片描述

很顯然我們使用紅黑樹之後相對於二叉樹來說,這棵樹更加平衡,搜索數據也更快,MySQL仍然不用該數據結構來維護索引數據是爲什麼?下邊來分析一下,徹底搞定

紅黑樹弊端

  • 目前表中有6條數據,所以需要將這六列存儲起來使用紅黑樹維護,那麼這棵樹的高度h=4,分別爲2、4、6、7四個節點,這裏沒有問題吧
  • 我們實際項目中數據不可能只有幾條,都是百萬條,千萬條數據,如果紅黑樹要維護百萬條,千萬條數據,那麼這可紅黑樹的高度h=?,很好計算,如果我們要想表中存儲100W條數據,也就是有100W個紅黑節點,每個節點有2個分支,將整棵樹撐滿2^n=1000000,n就是h深度,自己算一下吧
  • 通過上邊的分析,我們發現使用紅黑樹維護索引數據,這棵樹的深度太深,太深就~~~
  • 如果你要查找的數據在葉子節點,那麼查詢的次數也是蠻多的

通過上邊的紅黑樹我們可以發現數據越多數高度就越高,樹越高查詢數據需要的次數就越多,我們控制住樹的高度,就可以控制查詢的次數,這就是我們的B樹要來完成的偉業,所以大家不妨喝杯茶,思考一下在紅黑樹的基礎上將樹的高度控制在3-5層,進而存儲千萬條數據,若是你,將如何?

B樹

  二叉樹和紅黑樹都是一個節點上存儲一個數據,而B數是在紅黑樹的基礎上一個節點上存儲多個數據,所謂的B-Tree,BTree,B樹說的都是同一個東西,全稱Balance-tree譯爲平衡多路查找樹平衡爲左邊和右邊分佈均勻。·多路爲相對於二叉樹而言,二叉樹就是二路查找樹,查找時只有兩條路,而B-tree有多條路,即父節點有多個子節點,看圖說話
在這裏插入圖片描述

哦,是這張

在這裏插入圖片描述

  • 上邊18、25、60是一個節點,20、23是一個節點,同一個節點上存儲多個數據
  • data是在該節點上存儲的數據,如果是mysql中使用B樹存儲的就是數據的磁盤地址,就是我們要查找的那行數據在磁盤上的位置
  • 到索引中找到對應的節點,在節點中找到對應的數據,再獲取磁盤地址就可以找到這行數據

看看B樹存數據,存完之後一共有四個節點,2、4存一個節點,1一個節點,3一個節點,6、7一個節點,1比2小所以1在2的左側,3比2大比4小,所以在2右側4的左側,2、4就在同一個節點上存儲,6、7都比4大,所以存儲在一個節點上在4的右側

在這裏插入圖片描述

在看看B樹取數據,我們第一次取4根節點直接找到數據,第二次取7,找兩次確定所在節點

在這裏插入圖片描述

B樹特點

  • B-Tree可以顯著減少定位記錄時所經歷的中間過程,從而加快存取速度。這個數據結構一般用於數據庫的索引,綜合效率較高
  • 關鍵字集合分佈在整顆樹中,任何一個關鍵字出現且只出現在一個結點中
  • 節點中的數據從左到右依次排序
  • 搜索有可能在非葉子結點結束,葉子結點就是出度爲0的結點就是沒有子結點的結點
  • B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已經是葉子結點

B+Tree

  B+Tree是B-Tree的變種,MySQL就是使用B+Tree作爲索引數據結構,上圖
在這裏插入圖片描述

  • 非葉子節點並不存儲數據
  • 節點冗餘,葉子結點包含所有的非葉子節點
  • 數據在葉子結點存儲,而且葉子結點之間有箭頭指向

B+Tree存數據

在這裏插入圖片描述

B+Tree取數據

在這裏插入圖片描述

B+Tree爲什麼將數據存儲到葉子結點進行冗餘

data和節點都需要空間存儲,如果將data移除,可以存出更多節點,MySQL中使用的B+Tree每一節點可以存儲最多16KB數據可以通過SHOW GLOBAL STATUS LIKE 'InnoDb_page_size';這條SQL查詢,在16KB的情況下MySQL使用B+Tree可以存儲更多的索引元素,若表中id使用bigint當做索引佔8Byte,同時使用6Byte記錄該節點子節點位置那麼一個索引字段佔8+6=14Byte,16KB/14Byte=1170,每一個節點可以存儲1170個元素
在這裏插入圖片描述

B+Tree存滿可以存儲多少數據

上邊我們計算出每一個節點可以存儲1170個元素,每一個節點還有子節點,假如樹高爲3每一個索引佔1KB大小,這個1KB已經不小了,那麼它可以存儲1170*1170*16=2190W條數據,完全滿足我們千萬級別數據表的查詢

B+Tree葉子結點剪頭是幹嘛的

B+樹中的非葉子節點會冗餘一份在葉子節點中,並且葉子節點之間用指針相連,最開始的Hash不支持範圍查詢,二叉樹樹高很高,只有B樹跟B+有的一比,B樹一個節點可以存儲多個元素,相對於紅黑樹整體的樹高降低了,磁盤IO效率提高了。而B+樹是B樹的升級版,只是把非葉子節點冗餘一下,這麼做的好處是爲了提高範圍查找的效率。提高了的原因也無非是會有指針指向下一個節點的葉子節點

B+Tree特點

  • 所有關鍵字都出現在葉子節點的鏈表中(稠密索引),且鏈表中的關鍵字恰好有序
  • 在B-樹基礎上,爲葉子節點增加鏈表指針,所有關鍵字都在葉子節點中出現,非葉子節點作爲葉子節點的索引;
  • B+樹總是到葉子節點才命中數據不可能在非葉子節點命中
  • 更適合文件索引系統
  • 單一節點存儲的元素更多,使得查詢的IO次數更少,所以也就使得它更適合做爲數據庫MySQL的底層數據結構

到這我們介紹了Hash、二叉樹、紅黑樹、B-Tree、B+Tree每種數據結構,並且得出結論Mysql使用B+Tree作爲維護索引的數據結構,可以提高查詢索引時的磁盤IO效率,並且可以提高範圍查詢的效率,並且B+樹裏的元素也是有序的,接下來我們就說說Mysql中常見的兩種存儲引擎具體如何使用索引

Myisam存儲引擎索引實現

創建表最後一行ENGINE=MyISAM

CREATE TABLE `tb_myisam` (
  `id` int(11) NOT NULL,
  `col1` varchar(255) DEFAULT NULL,
  `col2` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

磁盤存儲

在這裏插入圖片描述

一個myisam表創建之後在磁盤上會有三個文件frmMYDMYI維護
frm:存儲表結構
MYD:存儲表數據
MYI:存儲表中索引

添加數據

insert into tb_myisam (id,col1,col2) VALUES
(2,"測試數據2","測試數據22"),
(4,"測試數據4","測試數據44"),
(5,"測試數據5","測試數據55"),
(7,"測試數據7","測試數據77"),
(1,"測試數據1","測試數據11"),
(3,"測試數據3","測試數據33"),
(6,"測試數據6","測試數據66");

查看數據

SELECT id,col1,col2 FROM tb_myisam

在這裏插入圖片描述

可以看到數據排序方式按照插入順序排序

Myisam索引維護

在id列創建索引,左上角爲數據維護在B+Tree中結構,右下角爲數據表數據,B+Tree葉子結點下方的data存儲的就是該行數據對應的磁盤地址,加入有一條SELECT id FROM tb_myisam WHERE id=3 會先到索引文件中找3這個節點,再取出對應磁盤地址,到MYD文件中找到這一行數據實現查詢。

在這裏插入圖片描述

InnoDB存儲引擎索引實現

創建表

CREATE TABLE `tb_innodb` (
  `id` int(11) NOT NULL,
  `col1` varchar(255) DEFAULT NULL,
  `col2` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

磁盤存儲

在這裏插入圖片描述

一個InnoDB表創建之後在磁盤上會有兩個文件frmibd維護
frm:存儲表結構
ibd:存儲表索引和數據

添加數據

insert into tb_innodb (id,col1,col2) VALUES
(2,"測試數據2","測試數據22"),
(4,"測試數據4","測試數據44"),
(5,"測試數據5","測試數據55"),
(7,"測試數據7","測試數據77"),
(1,"測試數據1","測試數據11"),
(3,"測試數據3","測試數據33"),
(6,"測試數據6","測試數據66");

查詢數據

在這裏插入圖片描述

mysql主鍵自動創建索引,innodb搜索引擎按照主鍵排序

InnoDB索引維護

InnoDB存儲引擎,表數據文件也就是ibd文件本身就是按照B+Tree組織的索引結構文件,葉節點包含了完整的數據記錄
在這裏插入圖片描述

如果我們InnoDB沒有索引怎麼辦,數據就無法存儲了嗎?

  大家應該通過一句話InnoDB表必須有主鍵,並且這個主鍵建議使用整型自動遞增的,如果你表中有主鍵那麼會在主鍵上添加索引來維護,如果你創建表時沒有指定主鍵,數據庫會在你的表中找到唯一數據那列去維護,如果找不到這樣的列,數據庫默認會自己增加一列來維護。
  推薦使用整型原因首先整型存儲所佔空間較小,而且比較排序時較快,可能有些公司在使用UUID當做主鍵等,UUID是一個隨機的字符串,在比較時需要先轉換再比較而且佔用空間大,所以不推薦使用
  推薦自動遞增是因爲我們葉子結點數據從左到右依次遞增排序,在做範圍查詢時比較方便,如果你的數值是隨機的就有可能修改樹原有的結構,導致分裂,裂開造成性能影響,可以看下圖舉例,我們最後添加8進來看變化
在這裏插入圖片描述

聯合索引長什麼樣?

  我們在開發項目時一般不創建單列索引,而是多個鍵創建聯合索引,現在只要你把聯合索引底層原理整明白,網上看的那些MySQL索引優化原則你就可以在底層原理上去理解,而不用再去背,我是很討厭背東西的,背完馬上忘沒意思,下圖就是聯合索引長相
在這裏插入圖片描述

假設我們聯合索引爲(col1,col2,col3),分別爲上圖綠色方格中的三行數據,分別根據col1,col2,col3三列排序,紫色爲其他非索引字段,這裏要明白的是聯合索引是按照什麼排序的,最左前綴法則爲什麼索引會失效,以及網上的索引優化文章爲什麼要求你這麼寫,我想經過你的思考應該都明白!一定要勤于思考!

文章思路

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

本文若有任何看不懂,或者有錯誤的地方歡迎大家評論區留言,我時時關注哦

我是添添,用你勤勞的雙手點個贊吧,這將是我創作更多優質文章的動力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章