mysql除以 再一次學習 MySQL 索引

2021-10-12 21:13:54 字數 2495 閱讀 1367

索引是一種提高我們查詢效率的資料結構。就好像是字典的目錄,一本幾百頁的字典,如果想快速查詢到某個字,總不能靠硬翻吧。

mysql 索引一般是雜湊表或 b+ 樹,常用的 innodb 引擎預設使用的是 b+ 樹來作為索引的資料結構。

如果使用 b+ 樹作為索引資料結構,那麼訪問或修改一條資料的時間複雜度是 o(log n),但是使用雜湊表作為索引結構幹這些活的時候,時間複雜度 o(1)。如果只是查一條資料或者修改一條資料,用雜湊表做索引肯定給力呀!但是一般業務系統不會這麼簡單。

在業務開發中,經常會遇到範圍查詢、排序查詢等需求。這個時候雜湊表索引就沒辦法高效的處理這些需求了。它只能通過掃表來實現這些功能,掃表應該是資料庫的噩夢吧。

mysql 使用 b+ 樹資料結構非葉子節點只儲存鍵值,葉子節點會儲存資料或者是主鍵。並且在葉子節點中鍵是按照順序儲存的,使得範圍查詢、排序查詢等變得異常簡單。

雖然雜湊表索引在操作單列資料的時候十分高效,但是需要範圍查詢、排序查詢的時候,b+ 樹資料結構顯然更合適。在我們業務開發中,不可能只操作一行資料。綜合考慮,還是 b+ 樹更適合作為索引的資料結構。

雜湊表索引不支援範圍查詢,不能利用索引來排序,不支援聯合索引最左匹配原則,如果重複鍵值比較多,還容易造成雜湊碰撞導致效率進一步降低。

b+ 樹的非葉子節點上只儲存鍵值,而 b 樹的非葉子節點上不僅儲存鍵值還儲存資料。在 mysql 資料庫中資料頁的大小是固定的,innodb 引擎資料頁預設大小為 16 kb。b+ 樹這種做法是為了讓樹的階數更大,讓樹更矮胖。進行查詢的時候,磁碟 io 次數就會減少,查詢效率也會更快。

b+ 樹的所有資料均儲存在葉子節點中,並且是按鍵值有序排列。但是 b 樹的資料分散在各個節點。進行範圍查詢,排序查詢的時候,b 樹的效率肯定不如 b+ 樹。

磁碟塊 1 中儲存 17 和 35 資料項,還有 p1、p2、p3 指標,p1 表示資料項小於 17 的磁碟塊,p2 表示資料項在 17 和 35 之間的資料項,p3 表示資料項大於 35 的資料項。非葉子節點不儲存資料,只儲存指引搜尋方向的資料項。

我們知道每次 io 讀取乙個資料頁的大小,也就是乙個磁碟塊。假設我們要查詢 29 這個資料項,首先進行第一次 io 將磁碟塊 1 讀進記憶體,發現 17 < 29 < 35,然後選用 p2 指標進行第二次 io 將磁碟塊 3 讀進記憶體,發現 26 < 29 < 30,然後選用 p2 指標將磁碟塊 8 讀進記憶體,在記憶體中做二分查詢,找到 29,結束查詢。

通過分析查詢過程,我們可以知道 io 次數和 b+ 樹的高度成正比。h 為樹的高度,m 為每個磁碟塊的資料項個數,n 為資料項總數。從下面的公式可以看出如果資料量 n 一定,m 越大相應的 h 就越小。

m 等於磁碟塊的大小除以資料項大小,由於磁碟塊大小一般是固定的,所以減小資料項大小才能使得 m 更大從而讓樹更矮胖。這也是為什麼 b+ 樹把真實資料放在葉子節點而不是非葉子節點的原因,如果真實資料放在非葉子結點,磁碟塊儲存的資料項會大幅度減少,樹就會增高相應查詢資料時的 io 次數就會變多。

這裡我們先假設 b+ 樹高為 2,即存在乙個根節點和若干個葉子節點,假設一行記錄的資料大小為 1 kb,那麼單個葉子節點(頁)中的記錄數等於 16 kb / 1 kb = 16 條資料。

然後要計算出非葉子節點能存放多少指標,我們假設主鍵 id 為 bigint 型別,長度為 8 位元組,而指標大小在 innodb 原始碼中設定為 6 位元組,這樣一共 14 位元組,我們乙個頁中能存放多少這樣的單元,其實就代表有多少指標,即 16 kb / 14 b = 1170。那麼可以算出一棵高度為 2 的 b+ 樹,大概就能存放下 1170 * 16 = 18720 條資料。

根據同樣的原理我們可以算出乙個高度為 3 的 b+ 樹就可以存放下 21902400 條資料。所以在 innodb 中 b+ 樹高度一般為 1 - 3 層,它就能滿足千萬級的資料儲存。在查詢資料時一次頁的查詢代表一次 io,所以通過主鍵索引查詢通常只需要 1 - 3 次邏輯 io 操作即可查詢到資料。

雜湊表索引操作單資料行的時候很快,但是不支援範圍查詢,不能利用索引來排序,不支援聯合索引最左匹配原則。

b 樹的資料可以儲存在非葉子節點中,範圍查詢時可能會有額外的隨機磁碟 io。而且由於真實資料存放在非葉子節點中,b 樹的高度肯定要高於同樣情況下的 b+ 樹。這樣也不利於提公升效率。

b+ 樹把真實資料儲存在葉子節點中是為了讓樹更矮胖,減少 io 次數,提公升效率。

這是我學習 mysql 索引結構記錄下的一些筆記,之後也還會總結一篇索引使用的相關注意事項。我發現閱讀完各路大神的文章之後,再自己寫一遍印象會深刻許多。雖然是炒舊飯,但也是炒給自己吃。希望大家多多給我鼓勵,哈哈哈哈哈。

再一次求助

編24點程式時遇到的問題 大佬好呀,python小白又來求助啦!這次的問題是在編24點時遇到的乙個問題 如何將使用者輸入的數字運算出結果?源 import random shuzi str one str random.randint 1,10 shuzi str two str random.ra...

開始再一次學習Git

這是乙個自己的管理自己學習經驗的文件。由於學習總是斷斷續續,所以沒有花時間整個學習git,這次勢必要搞懂git的相關的機制。我看的是廖雪峰的git教程,講的很不錯。接下來,開始做相關的筆記。可能會陸陸續續的進行修改 2017.3.24 今天下午,在上 軟體測試 的時候就在看git,本來是想在idea...

sql,linq基礎再一次學習

1 where 子句的作用是在對查詢結果進行分組前,將不符合where條件的行去掉,即在分組之前過濾資料,條件中不能包含聚組函式,使用where條件顯示特定的行。h ing 子句的作用是篩選滿足條件的組,即在分組之後過濾資料,條件中經常包含聚合函式,使用h ing 條件顯示特定的組,也可以使用多個分...