淺談MySQL聚簇索引
1. 什么是聚簇索引
數(shù)據(jù)庫的索引從不同的角度可以劃分成不同的類型,聚簇索引便是其中一種。
聚簇索引英文是 Clustered Index,有時(shí)候小伙伴們可能也會(huì)看到有人將之稱為聚集索引等,與之相對(duì)的是非聚簇索引或者二級(jí)索引。
聚簇索引并不是一種單獨(dú)的索引類型,而是一種數(shù)據(jù)的存儲(chǔ)方式。在 MySQL 的 InnoDB 存儲(chǔ)引擎中,所謂的聚簇索引實(shí)際上就是在同一個(gè) B+Tree 中保存了索引和數(shù)據(jù)行:此時(shí),數(shù)據(jù)放在葉子結(jié)點(diǎn)中,聚簇聚簇,意思就是說數(shù)據(jù)行和對(duì)應(yīng)的鍵值緊湊的存在一起。
假設(shè)我有如下數(shù)據(jù):
| id(主鍵) | username | age | address | gender |
|---|---|---|---|---|
| 1 | ab | 99 | 深圳 | 男 |
| 2 | ac | 98 | 廣州 | 男 |
| 3 | af | 88 | 北京 | 女 |
| 4 | bc | 80 | 上海 | 女 |
| 5 | bg | 85 | 重慶 | 女 |
| 6 | bw | 95 | 天津 | 男 |
| 7 | bw | 99 | ???/td> | 女 |
| 8 | cc | 92 | 武漢 | 男 |
| 9 | ck | 90 | 深圳 | 男 |
| 10 | cx | 93 | 深圳 | 男 |
那么它的聚簇索引大概就是這個(gè)樣子:

那么大家可以看到,葉子上既有主鍵值(索引)又有數(shù)據(jù)行,節(jié)點(diǎn)上則只有主鍵值(索引)。
小伙伴們想想,MySQL 表中的數(shù)據(jù)在磁盤中只可能保存一份,不可能保存兩份,所以,在一個(gè)表中,聚簇索引只可能有一個(gè),不可能有多個(gè)。
2. 聚簇索引和主鍵
有的小伙伴搞不清楚這兩者之間的關(guān)系,甚至將兩者劃等號(hào),這是一個(gè)巨大的誤區(qū)。
在有的數(shù)據(jù)庫中,支持開發(fā)者自由的選擇使用哪一個(gè)索引作為聚簇索引,但是 MySQL 中是不支持這個(gè)特性的。
在 MySQL 中,如果表本身就有設(shè)置主鍵,那么主鍵就是聚簇索引;如果表本身沒有設(shè)置主鍵,則會(huì)選擇表中的一個(gè)唯一且非空的索引來作為聚簇索引;如果表中連唯一非空的索引都沒有,那么就會(huì)自動(dòng)選擇表中的隱式主鍵來作為聚簇索引。關(guān)于 MySQL 中表的隱式主鍵,松哥會(huì)在將來的文章中和大家介紹。
不過一般來說,還是建議大家自己來為表設(shè)置主鍵,因?yàn)殡[式主鍵是自增的,自增的都會(huì)存在一個(gè)問題:在自增值上會(huì)存在非常高的鎖競(jìng)爭(zhēng)問題,主鍵的上界會(huì)稱為熱點(diǎn)數(shù)據(jù),因?yàn)樗械牟迦氩僮鞫家麈I自增,又不能重復(fù),所以會(huì)發(fā)生鎖競(jìng)爭(zhēng)進(jìn)而導(dǎo)致性能降低。
根據(jù)上面的介紹,我們可以總結(jié)出 MySQL 中聚簇索引和主鍵索引的關(guān)系如下:
- 聚簇索引不一定是主鍵索引。
- 主鍵索引一定是聚簇索引。
3. 聚簇索引優(yōu)缺點(diǎn)
先來說優(yōu)點(diǎn):
- 相互關(guān)聯(lián)的數(shù)據(jù)我們可以將之保存在一起。例如有一個(gè)用戶訂單表,我們可以根據(jù) 用戶 ID + 訂單 ID 來聚集所有數(shù)據(jù),用戶 ID 可能會(huì)重復(fù),訂單 ID 則不會(huì)重復(fù),這樣我們就能夠?qū)⒁粋€(gè)用戶相關(guān)的訂單數(shù)據(jù)都保存在一起,如果需要查詢一個(gè)用戶的所有訂單,就會(huì)非??欤恍枰倭康拇疟P IO 就可以做到。
- 不需要回表,因此數(shù)據(jù)訪問速度更快。在聚簇索引中,索引和數(shù)據(jù)都在同一棵 B+Tree 上,因此從聚簇索引中獲取到的數(shù)據(jù)比從非聚簇索引上獲取數(shù)據(jù)更快(非聚簇索引需要回表)。
- 對(duì)于第一點(diǎn)的案例,如果我們想根據(jù)用戶 ID 查詢到這個(gè)用戶所有的訂單 ID,那么此時(shí)都不用去到葉子結(jié)點(diǎn)了,因?yàn)橹Ч?jié)點(diǎn)上就有我們需要的數(shù)據(jù),所以直接利用覆蓋索引的特性,就可以讀取到需要的數(shù)據(jù)。
這些就是聚簇索引一些常見的優(yōu)點(diǎn),我們?cè)谌粘5谋碓O(shè)計(jì)中,其實(shí)應(yīng)該充分利用好這些優(yōu)點(diǎn)。
再來看看缺點(diǎn):
- 小伙伴們發(fā)現(xiàn),前面我們說的聚簇索引的優(yōu)勢(shì)主要是聚簇索引減少了 IO 次數(shù),從而提高了數(shù)據(jù)庫的性能,但是有的 IO 密集型應(yīng)用,可能直接上一個(gè)足夠大的內(nèi)存,把數(shù)據(jù)都讀取到內(nèi)存中操作,此時(shí)聚簇索引就沒有啥優(yōu)勢(shì)了。
- 隨機(jī)主鍵會(huì)導(dǎo)致頁分裂問題,主鍵順序插入的話,相對(duì)來說效率會(huì)高一些,因?yàn)樵?B+Tree 中只需要不斷往后面追加即可;但是主鍵如果是非順序插入的話,效率就會(huì)低很多,因?yàn)榭赡軙?huì)涉及到頁分裂問題。以上面那張圖為例,假設(shè)每個(gè)節(jié)點(diǎn)可以保存三條數(shù)據(jù),現(xiàn)在我們要插入一個(gè)主鍵是 4.5 的記錄,那么就需要把主鍵為 5 的值往后移動(dòng),進(jìn)而導(dǎo)致主鍵為 8 的節(jié)點(diǎn)也要往后移動(dòng)。頁分裂會(huì)導(dǎo)致數(shù)據(jù)插入效率降低并且占用更多的存儲(chǔ)空間。
- 非聚簇索引(二級(jí)索引)查詢的時(shí)候需要回表。因?yàn)橐粋€(gè)索引就是一棵索引樹,數(shù)據(jù)都在聚簇索引上,所以如果使用非聚簇索引進(jìn)行搜索,非聚簇索引的葉子上存儲(chǔ)的是主鍵值,先找到主鍵值,然后拿著主鍵值再來聚簇索引上搜索,這樣一共就查詢了兩棵索引樹,這就是回表。
4. 最佳實(shí)踐
看了上面的介紹,相信小伙伴已經(jīng)了解了,在使用聚簇索引的時(shí)候,主鍵最好不要使用 UUID 這種隨機(jī)字符串,使用 UUID 隨機(jī)字符串至少存在兩方面的問題:
- 插入效率低,因?yàn)椴迦肟赡軙?huì)導(dǎo)致頁分裂,這個(gè)前面已經(jīng)說過了。
- UUID 字符串所占用的存儲(chǔ)空間遠(yuǎn)遠(yuǎn)大于一個(gè) bigint,如果使用 UUID 來做主鍵,意味著在二級(jí)索引中,一個(gè)葉子結(jié)點(diǎn)能夠存儲(chǔ)的主鍵值就非常有限,進(jìn)而可能會(huì)導(dǎo)致樹增高,搜索時(shí)候 IO 次數(shù)增多,性能下降。
所以相對(duì)來說,主鍵自增會(huì)優(yōu)于 UUID。那么主鍵自增就是最完美的方案了嗎?很多小伙伴可能也聽說過一句話:沒有銀彈!所以,主鍵自增其實(shí)也有問題,具體什么問題,我們下便文章繼續(xù)。
到此這篇關(guān)于淺談MySQL聚簇索引的文章就介紹到這了,更多相關(guān)MySQL聚簇索引內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL Slave 觸發(fā) oom-killer解決方法
這篇文章主要介紹了MySQL Slave 觸發(fā) oom-killer解決方法,需要的朋友可以參考下2016-07-07
麒麟系統(tǒng)上安裝?MySQL?8.0.24的詳細(xì)步驟(避坑指南)
這篇文章主要介紹了麒麟系統(tǒng)上安裝MySQL8.0.24的詳細(xì)步驟,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
在MySQL?8.0版本中開啟遠(yuǎn)程登錄詳細(xì)的操作步驟
有時(shí)數(shù)據(jù)庫所在機(jī)器與項(xiàng)目運(yùn)行的機(jī)器不是同一個(gè),那么就涉及到遠(yuǎn)程鏈接數(shù)據(jù)庫了,下面這篇文章主要給大家介紹了關(guān)于在MySQL?8.0版本中開啟遠(yuǎn)程登錄詳細(xì)的操作步驟,需要的朋友可以參考下2024-04-04
MySQL重復(fù)數(shù)據(jù)提取最新一條技術(shù)方法詳解
在MySQL數(shù)據(jù)庫中清除重復(fù)數(shù)據(jù)是一項(xiàng)常見的任務(wù),下面這篇文章主要給大家介紹了關(guān)于MySQL重復(fù)數(shù)據(jù)提取最新一條的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
SQL Server COALESCE函數(shù)詳解及實(shí)例
這篇文章主要介紹了SQL Server COALESCE函數(shù)詳解及實(shí)例的相關(guān)資料,COALESCE函數(shù)比ISNULL更加強(qiáng)大,這個(gè)函數(shù)的確非常有用,需要的朋友可以參考下2016-12-12
MySQL數(shù)據(jù)庫10秒內(nèi)插入百萬條數(shù)據(jù)的實(shí)現(xiàn)
假設(shè)現(xiàn)在我們要向mysql插入500萬條數(shù)據(jù),如何實(shí)現(xiàn)高效快速的插入進(jìn)去?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2021-10-10
mysql left join的基本用法以及on與where的區(qū)別
我們?cè)趯憇ql語句的時(shí)候,總是無法避免使用到連接關(guān)鍵詞,比如內(nèi)連接、外連接,下面就是詳細(xì)的介紹,需要的朋友可以參考下2023-05-05

