Mysql索引底層原理分析

2021-09-19 08:13:12 字數 4739 閱讀 7492

mysql索引底層原理分析,

mysql索引的本質

mysql索引的底層原理

mysql索引的實戰經驗

面試問:資料庫中最常見的慢查詢優化方式是什麼? 同學a:加索引。

問:為什麼加索引能優化慢查詢?同學a:…不知道同學b:因為索引其實就是一種優化查詢的資料結構,比如mysql中的索引是用b+樹實現的,而b+樹就是一種資料結構,可以優化查詢速度,可以利用索引快速查詢資料,所以能優化查詢。

問:你知道哪些資料結構可以提高查詢速度?(聽到這個問題就感覺此處有坑…)同學b:雜湊表、完全平衡二叉樹、b樹、b+樹等等。

問:那這些資料結構既然都能優化查詢速度,那mysql種為何選擇使用b+樹?同學b:…不知道

提問show index from employees.titles;

640?wx_fmt=png

有乙個titles表,主鍵由empno,title,fromdate三個字段組成。

那麼以下幾個語句會用到索引嗎?

select*fromemployees.titleswhereemp_no=1

select*fromemployees.titleswheretitle=『1』

select*fromemployees.titleswhereemp_no='1』andtitle=1

select*fromemployees.titleswheretitle='1』andemp_no=1

為什麼雜湊表、完全平衡二叉樹、b樹、b+樹都可以優化查詢,為何mysql獨獨喜歡b+樹?

雜湊表有什麼特點?

假如有這麼一張表(表名:sanguo):

640?wx_fmt=png

現在對name欄位建立雜湊索引:

640?wx_fmt=jpeg

注意字段值所對應的陣列下標是雜湊演算法隨機算出來的,所以可能出現雜湊衝突。那麼對於這樣乙個索引結構,現在來執行下面的sql語句:

select*fromsanguowherename=『周瑜』

select*fromsanguowherename>『周瑜』

則無能為力,因為雜湊表的特點就是可以快速的精確查詢,但是不支援範圍查詢。

640?wx_fmt=jpeg

圖中的每乙個節點實際上應該有四部分:

左指標,指向左子樹

鍵值鍵值所對應的資料的儲存位址

右指標,指向右子樹

另外需要提醒的是,二叉樹是有順序的,簡單的說就是「左邊的小於右邊的」假如我們現在來查詢『周瑜』,需要找2次(第一次曹操,第二次周瑜),比雜湊表要多一次。而且由於完全平衡二叉樹是有序的,所以也是支援範圍查詢的。

640?wx_fmt=jpeg

可以發現同樣的元素,b樹的表示要比完全平衡二叉樹要「矮」,原因在於b樹中的乙個節點可以儲存多個元素。

640?wx_fmt=jpeg

我們可以發現同樣的元素,b+樹的表示要比b樹要「胖」,原因在於b+樹中的非葉子節點會冗餘乙份在葉子節點中,並且葉子節點之間用指標相連。

那麼b+樹到底有什麼優勢呢?

這裡我們用「反證法」,假如我們現在就用完全平衡二叉樹作為索引的資料結構,我們來看一下有什麼不妥的地方。實際上,索引也是很「大」的,因為索引也是儲存元素的,我們的乙個表的資料行數越多,那麼對應的索引檔案其實也是會很大的,實際上也是需要儲存在磁碟中的,而不能全部都放在記憶體中,所以我們在考慮選用哪種資料結構時,我們可以換乙個角度思考,哪個資料結構更適合從磁碟中讀取資料,或者哪個資料結構能夠提高磁碟的io效率。回頭看一下完全平衡二叉樹,當我們需要查詢「張飛」時,需要以下步驟

從磁碟中取出「曹操」到記憶體,cpu從記憶體取出資料進行筆記,「張飛」<「曹操」,取左子樹(產生了一次磁碟io)

從磁碟中取出「周瑜」到記憶體,cpu從記憶體取出資料進行筆記,「張飛」>「周瑜」,取右子樹(產生了一次磁碟io)

從磁碟中取出「孫權」到記憶體,cpu從記憶體取出資料進行筆記,「張飛」>「孫權」,取右子樹(產生了一次磁碟io)

從磁碟中取出「黃忠」到記憶體,cpu從記憶體取出資料進行筆記,「張飛」=「張飛」,找到結果(產生了一次磁碟io)

同理,回頭看一下b樹,我們發現只傳送三次磁碟io就可以找到「張飛」了,這就是b樹的優點:乙個節點可以儲存多個元素,相對於完全平衡二叉樹所以整棵樹的高度就降低了,磁碟io效率提高了。

而b+樹是b樹的公升級版,只是把非葉子節點冗餘一下,這麼做的好處是為了提高範圍查詢的效率。

到這裡可以總結出來,mysql選用b+樹這種資料結構作為索引,可以提高查詢索引時的磁碟io效率,並且可以提高範圍查詢的效率,並且b+樹里的元素也是有序的。

那麼,乙個b+樹的節點中到底存多少個元素合適呢?

其實也可以換個角度來思考b+樹中乙個節點到底多大合適?

答案是:b+樹中乙個節點為一頁或頁的倍數最為合適。因為如果乙個節點的大小小於1頁,那麼讀取這個節點的時候其實也會讀出1頁,造成資源的浪費;如果乙個節點的大小大於1頁,比如1.2頁,那麼讀取這個節點的時候會讀出2頁,也會造成資源的浪費;所以為了不造成浪費,所以最後把乙個節點的大小控制在1頁、2頁、3頁、4頁等倍數頁大小最為合適。

那麼,mysql中b+樹的乙個節點大小為多大呢?

這個問題的答案是「1頁」,這裡說的「頁」是mysql自定義的單位(其實和作業系統類似),mysql的innodb引擎中一頁的預設大小是16k(如果作業系統中一頁大小是4k,那麼mysql中1頁=作業系統中4頁),可以使用命令show global status like 『innodbpagesize』; 檢視。

並且還可以告訴你的是,乙個節點為1頁就夠了。

640?wx_fmt=png

640?wx_fmt=png

mysql中myisam和innodb使用b+樹

640?wx_fmt=jpeg

通常我們認為b+樹的非葉子節點不儲存資料,只有葉子節點才儲存資料;而b樹的非葉子和葉子節點都會儲存資料,會導致非葉子節點儲存的索引值會更少,樹的高度相對會比b+樹高,平均的i/o效率會比較低,所以使用b+樹作為索引的資料結構,再加上b+樹的葉子節點之間會有指標相連,也方便進行範圍查詢。上圖的data區域兩個儲存引擎會有不同。

myisam中的b+樹

myisam中葉子節點的資料區域儲存的是資料記錄的位址

主鍵索引

640?wx_fmt=png

輔助索引

640?wx_fmt=png

myisam儲存引擎在使用索引查詢資料時,會先根據索引查詢到資料位址,再根據位址查詢到具體的資料。並且主鍵索引和輔助索引沒有太多區別。

innodb中的b+樹

innodb中主鍵索引的葉子節點的資料區域儲存的是資料記錄,輔助索引儲存的是主鍵值

主鍵索引

640?wx_fmt=png

640?wx_fmt=png

輔助索引

640?wx_fmt=jpeg

innodb中的主鍵索引和實際資料時繫結在一起的,也就是說innodb的乙個表一定要有主鍵索引,如果乙個表沒有手動建立主鍵索引,innodb會檢視有沒有唯一索引,如果有則選用唯一索引作為主鍵索引,如果連唯一索引也沒有,則會預設建立乙個隱藏的主鍵索引(使用者不可見)。另外,innodb的主鍵索引要比myisam的主鍵索引查詢效率要高(少一次磁碟io),並且比輔助索引也要高很多。所以,我們在使用innodb作為儲存引擎時,我們最好:

手動建立主鍵索引

盡量利用主鍵索引查詢

回到我們的問題:為什麼乙個節點為1頁(16k)就夠了?

對著上面mysql中innodb中對b+樹的實際應用(主要看主鍵索引),可以發現b+樹中的乙個節點儲存的內容是:

非葉子節點:主鍵+指標

葉子節點:資料

那麼,假設我們一行資料大小為1k,那麼一頁就能存16條資料,也就是乙個葉子節點能存16條資料;再看非葉子節點,假設主鍵id為bigint型別,那麼長度為8b,指標大小在innodb原始碼中為6b,一共就是14b,那麼一頁裡就可以儲存16k/14=1170個(主鍵+指標),那麼一顆高度為2的b+樹能儲存的資料為:117016=18720條,一顆高度為3的b+樹能儲存的資料為:11701170*16=21902400(千萬級條)。所以在innodb中b+樹高度一般為1-3層,它就能滿足千萬級的資料儲存。在查詢資料時一次頁的查詢代表一次io,所以通過主鍵索引查詢通常只需要1-3次io操作即可查詢到資料。所以也就回答了我們的問題,1頁=16k這麼設定是比較合適的,是適用大多數的企業的,當然這個值是可以修改的,所以也能根據業務的時間情況進行調整。

最左字首原則

我們模擬資料建立乙個聯合索引 select*,concat(right(emp_no,1),"-",right(title,1),"-",right(from_date,2))fromemployees.titles limit10;

640?wx_fmt=png

那麼對應的b+樹為

640?wx_fmt=png

我們判斷乙個查詢條件能不能用到索引,我們要分析這個查詢條件能不能利用某個索引縮小查詢範圍

對於 selectfromemployees.titleswhereemp_no=1是能用到索引的,因為它能利用上面的索引所有查詢範圍,首先和第乙個節點「4-r-01」比較,1<4,所以可以直接確定結果在左子樹,同理,依次按順序進行比較,逐步可以縮小查詢範圍。對於 selectfromemployees.titleswheretitle='1』是不能用到索引的,因為它不能用到上面的所以,和第一節點進行比較時,沒有empno這個欄位的值,不能確定到底該去左子樹還是右子樹繼續進行查詢。對於 select*fromemployees.titleswheretitle='1』andemp_no=1是能用到索引,按照我們的上面的分析,先用title='1』這個條件和第乙個節點進行比較,是沒有結果的,但是mysql會對這個sql進行優化,優化之後會將empno=1這個條件放到第一位,從而可以利用索引。

mysql 索引底層原理分析

一 區分幾個概念 1 頁 1 頁號 記錄當前是第幾頁。2 頁目錄 記錄行資料,標記行開始的最小主鍵索引資料。3 行資料 標識每一行的資料。1 111a 標識第一行資料,1 表示主鍵索引。行與行資料使用鍊錶有序相連。2 b 樹 多個頁相互連線在一起,為了方便查詢會再向上生成乙個新的類似頁的東西,記錄頁...

MySQL索引底層(二) 索引底層原理

聚集索引 上次我們講到了主鍵的索引,我們可以執行一下sql語句 explain select from t user where a 1 我們可以看到這條sql走的是主鍵的索引,而在mysql的innodb中,主鍵索引則是聚集索引,資料的物理順序與鍵值的邏輯 索引 順序相同,其實就是說主鍵索引跟其他...

MySQL索引底層原理

通過hash演算法,能快速檢索資料 資料碰撞問題用鏈位址法 無法進行範圍搜尋 解決雜湊索引無法範圍搜尋的問題 極端情況下會退化成線性鍊錶,自增主鍵必然會導致極端情況 會自動調整樹形態,使其保持平衡,調整會消耗效能 無法完全解決二叉查詢樹的問題 絕對平衡的二叉樹,更耗效能 根本解決了紅黑數的問題 由於...