MySQL - 剖析MySQL索引底层数据结构
文章目錄
- 更多干貨
- Pre
- 索引的數(shù)據(jù)結(jié)構(gòu)選型
- 二叉樹 ?
- 紅黑樹 ?
- B-Tree ?
- B+Tree
- Hash表
- 搞定MySQL
更多干貨
帶你搞定MySQL實(shí)戰(zhàn),輕松對應(yīng)海量業(yè)務(wù)處理及高并發(fā)需求,從容應(yīng)對大場面試
Pre
什么是索引?
通俗的說就是為了提高效率專門設(shè)計(jì)的一種 排好序的數(shù)據(jù)結(jié)構(gòu)。
怎么理解呢?
舉個(gè)例子哈
如上數(shù)據(jù) ,假設(shè)有個(gè)SQL
select * from t where col2 = 22 ;如果沒有索引的話,是不是得逐行進(jìn)行全表掃描,走磁盤IO…
如果加上一個(gè)合適的索引呢?
比如用一個(gè)二叉樹
二叉樹我們知道,右邊的比左邊大
那執(zhí)行剛才的SQL的話,第一條記錄是34 ,那我們查找的是22, 是不是就只要到它的左邊查找即可,因?yàn)橛疫叺臄?shù)據(jù)都比34大,肯定沒有22 ,找到22 以后, 搞定 ,I/O次數(shù)是不是比剛才的全表掃描的次數(shù)少很多,那效率自然就高了。
索引的數(shù)據(jù)結(jié)構(gòu)選型
二叉樹 ?
可以用二叉樹嗎? 我們知道MySQL一般都有自增主鍵 ,id之類的字段
我們來演示下使用二叉樹來存儲(chǔ)這種自增的數(shù)據(jù)的話,會(huì)怎樣?
https://www.cs.usfca.edu/~galles/visualization/BST.html
那查詢
select * from t where id = 7自增主鍵的時(shí)候 這個(gè)二叉樹已經(jīng)退化成鏈表了。。。。。
想想,一個(gè)幾百萬數(shù)據(jù)量的表 ,查找某個(gè)大一點(diǎn)的id , 逐個(gè)查找比對 (這些數(shù)據(jù)也是存儲(chǔ)在磁盤上的,還得從磁盤上撈啊) 這I/O 這效率可想而知吧…
二叉樹 pass ,不考慮了
既然退化成鏈表了,那試試帶有平衡功能的樹 二叉平衡樹 (紅黑樹)?
自增主鍵, 退化為為鏈表
紅黑樹 ?
二叉樹既然在某些情況下會(huì)退化成鏈表, 那如果這棵樹能自動(dòng)平衡呢?
這樣子是不可能變成鏈表了,
同樣 查詢
select * from t where id = 7三次磁盤I/O即可找到, 比剛才二叉樹的七次是少了些哈 ,自然查找效率也比二叉樹高了
可如果數(shù)據(jù)量幾百萬 上千萬呢?
這棵樹 得多高哇。。。
數(shù)據(jù)量大, 樹高問題
那既然樹高不好, 是不是如果可以控制樹的高度(比如 3 到4層的高度,這樣查詢起來還能接受),讓每一層能存儲(chǔ)更多的數(shù)據(jù),然后再分裂,這樣的話數(shù)據(jù)量相乘起來,也是不少了對吧,這樣就能存儲(chǔ)更多的數(shù)據(jù),這樣會(huì)不會(huì)好一點(diǎn)? ----> B-Tree
B-Tree ?
- 葉節(jié)點(diǎn)具有相同的深度, 葉節(jié)點(diǎn)之間指針為空
- 所有索引元素不重復(fù)
- 節(jié)點(diǎn)中的數(shù)據(jù)索引從左到右遞增排列
葉子節(jié)點(diǎn)之間的沒有指針,區(qū)別于B+樹。
data存儲(chǔ)的是數(shù)據(jù)對應(yīng)的磁盤地址, k-v結(jié)構(gòu)。
我們來看下B-Tree的插入 (Max.Degree 設(shè)置為3 即 元素到了3個(gè)就分裂 )
查找一下
3次
MySQL也沒有使用B-Tree , 因?yàn)?br />
除了存儲(chǔ)索引以外,還存儲(chǔ)了data(數(shù)據(jù)對應(yīng)的磁盤地址) , 為了更多的存儲(chǔ)數(shù)據(jù),MySQL對B-Tree進(jìn)行了很多改造
由此演進(jìn)出了 B+Tree ,將data部分僅保留在葉子節(jié)點(diǎn)上,這樣的話同等的頁可以存儲(chǔ)更多而索引數(shù)據(jù)。
B+Tree
- 非葉子節(jié)點(diǎn)不存儲(chǔ)data,只存儲(chǔ)索引(冗余),可以放更多的索引
- 葉子節(jié)點(diǎn)包含所有索引字段
- 葉子節(jié)點(diǎn)用指針連接,提高區(qū)間訪問的性能
數(shù)據(jù)僅存儲(chǔ)在葉子節(jié)點(diǎn), data可能是磁盤地址也可能是其他的列數(shù)據(jù),這個(gè)和存儲(chǔ)引擎有關(guān)系。
葉子節(jié)點(diǎn)之間有指針相連。
我們來算下 3層高的B+Tree能存儲(chǔ)多少數(shù)據(jù)結(jié)構(gòu)
假設(shè)是BigInt類型的數(shù)據(jù)
BigInt 占 8個(gè)字節(jié) ,同時(shí)還是用6個(gè)字節(jié)存儲(chǔ)了它指向的數(shù)據(jù)的物理地址
MySQL在使用innodb引擎的時(shí)候頁大小默認(rèn)是16K ,查詢?nèi)缦?/p> mysql> SHOW GLOBAL STATUS like 'Innodb_page_size'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | Innodb_page_size | 16384 | +------------------+-------+ 1 row in set (0.00 sec)mysql>
假設(shè) 樹高為3 , 這樣的話,第一層即可以存儲(chǔ) 16KB * 1024 / (8B + 6B) = 1170
同樣的第二層也是1170 (第二層不是葉子結(jié)點(diǎn),不存儲(chǔ)數(shù)據(jù))
第三層,存儲(chǔ)數(shù)據(jù),一般情況下一行數(shù)據(jù)的大小肯定不會(huì)超過1KB,那我們就按照1KB算吧
3層高的B+Tree , 存儲(chǔ)BitInt可以存儲(chǔ) 1170 * 1170 * 16 = 2千1 百萬。。。。這效率還是可以的哈
想一想 如果是4層高的數(shù) 1170 * 1170 * 1170 * 16 = 250多億數(shù)據(jù)。.。。。
當(dāng)然了 都是估算, 如果換成其他類型的數(shù)據(jù),每個(gè)表的行數(shù)據(jù)的大小都是相關(guān)的,這也就是我們通常說的 MySQL的表到千萬級別就要分庫分表的理論依據(jù)了。
我們看下B+Tree的插入和查找
Hash表
- 對索引的key進(jìn)行一次hash計(jì)算就可以定位出數(shù)據(jù)存儲(chǔ)的位置
- 很多時(shí)候Hash索引要比B+ 樹索引更高效
- 僅能滿足 “=”,“IN”,不支持范圍查詢
- hash沖突問題
對索引字段進(jìn)行hash以后, 還存儲(chǔ)了數(shù)據(jù)對引得磁盤地址。
一般請款下,hash 比 b+tree的效率要高 ,但工作中絕大部分還是使用的B+Tree , 因?yàn)?strong>hash對范圍查找不是很友好,還要全表掃描。
為啥B+Tree 支持范圍查找?
我們知道B+Tree的葉子節(jié)點(diǎn) 有指針相連,從根節(jié)點(diǎn)找到對應(yīng)的葉子節(jié)點(diǎn)后, 加上節(jié)點(diǎn)本身就是排好序的,所以范圍查找就恨輕松了。
B-Tree 沒有指針相連,所以要想范圍查找,還得從根節(jié)點(diǎn)重新找,效率肯定比B+樹低 。
搞定MySQL
總結(jié)
以上是生活随笔為你收集整理的MySQL - 剖析MySQL索引底层数据结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APM - Javassist 入门 生
- 下一篇: MySQL - MySQL不同存储引擎