成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL索引數(shù)據(jù)結(jié)構(gòu)入門

數(shù)據(jù)庫(kù) MySQL
Hash 索引在 MySQL 中主要是 Memory 和 NDB 引擎支持,InnoDB 索引本身是 不支持的,但是 InnoDB 索引有一個(gè)特性叫做自適應(yīng)哈希索引,自適應(yīng)三個(gè)字意味著整個(gè)過(guò)程是全自動(dòng)的,不需要開(kāi)發(fā)者配置。

之前松哥寫過(guò)一個(gè) MySQL 系列,但是當(dāng)時(shí)是基于 MySQL5.7 的,最近有空在看 MySQL8 的文檔,發(fā)現(xiàn)和 MySQL5.7 相比還是有不少變化,同時(shí) MySQL 又是小伙伴們?cè)诿嬖嚂r(shí)一個(gè)非常重要的知識(shí)點(diǎn),因此松哥打算最近再抽空和小伙伴們聊一聊 MySQL,講講原理,講講優(yōu)化,我會(huì)從最基本最簡(jiǎn)單的開(kāi)始,和大家梳理 MySQL 中常見(jiàn)的面試知識(shí)點(diǎn)。

本文我們就先從最簡(jiǎn)單的索引開(kāi)始吧~

1. 什么是索引

說(shuō)到索引,最常見(jiàn)的例子就是查字典,當(dāng)我們需要查詢某一個(gè)字的含義時(shí),正常操作都是先根據(jù)字典的索引,找到該字在哪一頁(yè),然后直接翻到該頁(yè)就行了。如果沒(méi)有這個(gè)索引的話,那么我們就得一頁(yè)一頁(yè)的翻字典,直到找到該字。很明顯,相對(duì)于第一種方案,第二種方案效率就要低很多了。

數(shù)據(jù)庫(kù)中的索引也是類似的。

索引,我們也稱之為 index 或者 key,當(dāng)數(shù)據(jù)量比較少的時(shí)候,索引對(duì)于查詢產(chǎn)生的效果并不明顯,所以索引常常被人所忽略,但是當(dāng)數(shù)據(jù)量比較大的時(shí)候,一個(gè)優(yōu)秀的索引對(duì)查詢產(chǎn)生的影響就是非常明顯的了。在我們所掌握的各種 SQL 優(yōu)化策略中,索引對(duì) SQL 優(yōu)化產(chǎn)生的效果算是最好的了,用好索引,SQL 性能可能會(huì)提升好幾個(gè)數(shù)量級(jí)。

這里有的小伙伴可能會(huì)有一個(gè)疑惑,很多索引優(yōu)化策略都是針對(duì)傳統(tǒng)的機(jī)械硬盤的,然而現(xiàn)在我們大部分都是固態(tài)硬盤(SSD),很多針對(duì)機(jī)械硬盤的優(yōu)化策略在 SSD 上似乎并沒(méi)有必要,那還有必要去考慮索引優(yōu)化嗎?答案當(dāng)然是有!無(wú)論是用什么樣的磁盤,索引優(yōu)化的整體原則都是不變的,只不過(guò)在 SSD 上,如果你的索引沒(méi)有創(chuàng)建好,那么它對(duì)查詢的影響不像對(duì)機(jī)械硬盤那么糟糕。

2. 索引的數(shù)據(jù)結(jié)構(gòu)

2.1 B+Tree 和 B-Tree

小伙伴們知道,由于 MySQL 中的存儲(chǔ)引擎設(shè)計(jì)成了可插拔的形式,任何機(jī)構(gòu)和個(gè)人如果你有能力,都可以設(shè)計(jì)自己的存儲(chǔ)引擎,而 MySQL 的索引是在存儲(chǔ)引擎層實(shí)現(xiàn)的,而不是在服務(wù)器層實(shí)現(xiàn)的,所以不同存儲(chǔ)引擎的索引工作方式都不一樣,甚至,相同類型的索引,在不同的存儲(chǔ)引擎中實(shí)現(xiàn)方案都不同。

本文松哥主要和小伙伴們介紹我們?nèi)粘i_(kāi)發(fā)中最最常見(jiàn)的 InnoDB 存儲(chǔ)引擎中的索引。

小伙伴們知道,InnoDB 存儲(chǔ)引擎的索引數(shù)據(jù)結(jié)構(gòu)是一個(gè) B+Tree,至于什么是 B+Tree,這并非本文的重點(diǎn),我這里不啰嗦,不了解 B+Tree 的小伙伴可以自行搜索一下學(xué)習(xí)一下。

假設(shè)我有如下數(shù)據(jù):

username

age

address

gender

ab

99

深圳


ac

98

廣州


af

88

北京


bc

80

上海


bg

85

重慶


bw

95

天津


bw

99

海口


cc

92

武漢


ck

90

深圳


cx

93

深圳


現(xiàn)在我給 username 和 age 字段建立聯(lián)合索引,那么最終數(shù)據(jù)在磁盤上的存儲(chǔ)結(jié)構(gòu)是 B+Tree,為了小伙伴能夠更好的理解 B+Tree 和 B-Tree,我畫了如下兩張圖:

圖片

這兩張圖看懂了,InnoDB 存儲(chǔ)引擎的索引我覺(jué)得基本上都搞懂了 80% 了,松哥來(lái)和大家稍微梳理一下這張圖:

  1. 首先這兩張圖都是一個(gè)多路平衡查找樹(shù),即,不是二叉樹(shù),是多叉樹(shù)。
  2. 綠色的方塊表示指向下一個(gè)節(jié)點(diǎn)的指針;紅色的方塊表示指向下一個(gè)葉子節(jié)點(diǎn)的指針(B-Tree 中不存在該部分);帶陰影的矩形則表示索引數(shù)據(jù)。
  3. B+Tree 非葉子節(jié)點(diǎn)只保存關(guān)鍵字的索引和指向下一個(gè)節(jié)點(diǎn)的指針(綠色區(qū)域),所有的數(shù)據(jù)最終都會(huì)保存到葉子節(jié)點(diǎn)。因此在具體的搜索過(guò)程中,所有數(shù)據(jù)都必須要到葉子節(jié)點(diǎn)才能獲取到,因此每次數(shù)據(jù)查詢所需的 IO 次數(shù)都一樣,這也就意味著 B+Tree 的查詢速度比較穩(wěn)定。

如果是 B-Tree 則分支節(jié)點(diǎn)上也保存了指向具體數(shù)據(jù)的指針,并且分支節(jié)點(diǎn)上出現(xiàn)的索引數(shù)據(jù)不會(huì)再次出現(xiàn)在葉子節(jié)點(diǎn)中,所以搜索的時(shí)候可能搜索到分支節(jié)點(diǎn)就找到需要的數(shù)據(jù)了,搜索效率不穩(wěn)定,如 af 在分支節(jié)點(diǎn)上就找到了,而 ac 則要到葉子節(jié)點(diǎn)上才能找到)。

  1. B+Tree 中,由于分支節(jié)點(diǎn)只保存索引數(shù)據(jù)和指向下一個(gè)節(jié)點(diǎn)的指針,所以在相同的磁盤空間中,能夠指向更多的子節(jié)點(diǎn),這就意味樹(shù)的高度更低,搜索所需要的 IO 次數(shù)更少,搜索效率更高。

B-Tree 中,由于分支節(jié)點(diǎn)不僅保存索引數(shù)據(jù)和指向下一個(gè)節(jié)點(diǎn)的指針,還保存了指向具體數(shù)據(jù)的指針,所以在相同的空間下能夠指向的子節(jié)點(diǎn)數(shù)量就少于 B+Tree,這就意味著相同的數(shù)據(jù)量,B-Tree 樹(shù)高更高,搜索所需的 IO 次數(shù)更多,搜索效率低。

  1. B+Tree 葉子節(jié)點(diǎn)的關(guān)鍵字從小到大按順序排列,左邊結(jié)尾數(shù)據(jù)都會(huì)保存右邊節(jié)點(diǎn)開(kāi)始數(shù)據(jù)的指針(紅色區(qū)域),這個(gè)指針在范圍搜索的時(shí)候非常有用,例如想搜索姓名在 ac~bc 之間的數(shù)據(jù),按照樹(shù)找到第一個(gè)節(jié)點(diǎn) ac 之后,順著指針一直往后找,找到第一個(gè)不滿足條件的數(shù)據(jù)結(jié)束。

如果是 B-Tree 則沒(méi)有 ac 指向 bc 的指針,需要先回到分支節(jié)點(diǎn) af 再繼續(xù)向下搜索,效率就會(huì)低很多。

  1. B+Tree 的葉子節(jié)點(diǎn)都是有序排列的,所以 B+Tree 對(duì)于數(shù)據(jù)的排序有著更好的支持。

B-Tree 由于有一部分?jǐn)?shù)據(jù)保存在分支節(jié)點(diǎn)中,葉子節(jié)點(diǎn)并不是完整的數(shù)據(jù),所以對(duì)于排序、范圍搜索的支持并不如 B+Tree。

  1. B+Tree 數(shù)據(jù)劃分的原則是左閉右開(kāi),以 (af,88) 這個(gè)節(jié)點(diǎn)為例,小于 (af,88) 節(jié)點(diǎn)的在左邊,大于等于 (af,88) 節(jié)點(diǎn)的在右邊。

B-Tree 則是左開(kāi)右開(kāi)。

  1. B+Tree 全表掃描更快,因?yàn)樗袛?shù)據(jù)都出現(xiàn)在葉子節(jié)點(diǎn)上,并且葉子節(jié)點(diǎn)之間還有指針相連,直接遍歷即可。

B-Tree 在全表掃描的時(shí)候則需要對(duì)樹(shù)的每一層進(jìn)行遍歷才能讀到所有數(shù)據(jù)。

  1. 葉子節(jié)點(diǎn)指向數(shù)據(jù)的指針,如果是聚簇索引,則指向的是表中一條完整的記錄;如果是非聚簇索引,則指向的是具體的主鍵值。在以非聚簇索引為依據(jù)進(jìn)行搜索的時(shí)候,先找到記錄的主鍵值,再根據(jù) 主鍵值去聚簇索引找到完整的記錄,這個(gè)過(guò)程就是回表(InnoDB 中)。

好了,相信通過(guò)上面八點(diǎn)的介紹,大家對(duì)于 B-Tree 和 B+Tree 已經(jīng)有了基本的認(rèn)知了。

當(dāng)我們想要搜索一條記錄的時(shí)候,順著根節(jié)點(diǎn)從上往下掃描樹(shù),比直接遍歷一條一條的記錄顯然是要快很多。

說(shuō)一個(gè)不太恰當(dāng)?shù)谋扔鳎琈ySQL 中的數(shù)據(jù)存儲(chǔ),就像是通過(guò)一個(gè)鏈表把所有數(shù)據(jù)按照順序串到一起,然后在這個(gè)鏈表上面又架了一個(gè)多路平衡查找樹(shù)的感覺(jué),搜索的時(shí)候,按照鏈表一個(gè)一個(gè)找,就是全表掃描;從樹(shù)的根節(jié)點(diǎn)開(kāi)始找,就是用索引。

2.2 樹(shù)高問(wèn)題

一個(gè)經(jīng)典的問(wèn)題,高度為 3 的 B+Tree 大概可以保存多少條數(shù)據(jù)?

計(jì)算機(jī)在存儲(chǔ)數(shù)據(jù)的時(shí)候,最小存儲(chǔ)單元是扇區(qū),一個(gè)扇區(qū)的大小是 512 字節(jié),而文件系統(tǒng)(例如 XFS/EXT4)最小單元是塊,一個(gè)塊的大小是 4KB。但是 InnoDB 在進(jìn)行磁盤操作的時(shí)候,并不是以扇區(qū)或者塊為依據(jù)的,InnoDB 在進(jìn)行磁盤操作的時(shí)候,是以頁(yè)為單位的,有時(shí)候也稱作邏輯頁(yè),每個(gè)邏輯頁(yè)的大小默認(rèn)是 16KB,即四個(gè)塊。這就意味著,InnoDB 在實(shí)際操作磁盤的時(shí)候,每次從磁盤上讀取數(shù)據(jù),至少讀取 16KB,每次向磁盤上寫數(shù)據(jù),也至少寫 16KB,并不是你需要 1KB 就讀取 1KB,即使你只需要 1KB 的數(shù)據(jù),InnoDB 也會(huì)從磁盤中將 16KB 的數(shù)據(jù)讀取到內(nèi)存中。

通過(guò)如下命令我們可以查看 MySQL 中 InnoDB 存儲(chǔ)引擎邏輯頁(yè)的大小:

圖片

16384/16=1024

前面的結(jié)論沒(méi)問(wèn)題。

以聚簇索引為例,現(xiàn)在我們假設(shè)數(shù)據(jù)庫(kù)中一條記錄的大小是 1KB,那么一個(gè)邏輯頁(yè)就可以存 16 條數(shù)據(jù)(葉子節(jié)點(diǎn))。

對(duì)于非葉子節(jié)點(diǎn)存儲(chǔ)的則是主鍵值+指針,在 InnoDB 中,一個(gè)指針的大小是 6 個(gè)字節(jié),假設(shè)我們的主鍵是 bigint ,那么主鍵占 8 個(gè)字節(jié),當(dāng)然還有其他一些頭信息也會(huì)占用字節(jié)我們這里就不考慮了,我們大概算一下,小伙伴們心里有數(shù)即可:

16*1024/(8+6)=1170

即一個(gè)非葉子節(jié)點(diǎn)可以指向 1170 個(gè)子節(jié)點(diǎn),那么一個(gè)三層的 B+Tree 可以存儲(chǔ)的數(shù)據(jù)量為:

1170*1170*16=21902400

可以存儲(chǔ) 2100萬(wàn) 條數(shù)據(jù)。

在 InnoDB 存儲(chǔ)引擎中,B+Tree 的高度一般為 2-4 層,這就可以滿足千萬(wàn)級(jí)的數(shù)據(jù)的存儲(chǔ),查找數(shù)據(jù)的時(shí)候,一次頁(yè)的查找代表一次 IO,那我們通過(guò)主鍵索引查詢的時(shí)候,其實(shí)最多只需要 2-4 次 IO 操作就可以了。

2.3 什么樣的搜索可以用到索引?

根據(jù)前面的介紹,我們可以得出結(jié)論,在以下類型的搜索中,會(huì)用到索引:

  • 全值匹配

如上圖中,如果我們要搜索 username 為 ac 且 age 為 98 的用戶,就可以直接使用索引精確定位到。

  • 最左匹配

如果我們只是想搜索 username 為 ac 的用戶,很明顯也可以使用上圖索引,因?yàn)橛脩裘怯行虻摹T谏蠄D中,username 和 age 組成了聯(lián)合索引,其中 username 在前,age 在后,所以索引是先按照 username 進(jìn)行排序,username 相同的時(shí)候,再按照 age 進(jìn)行排序的(如 bw 這個(gè)用戶),如果我們按照 username 進(jìn)行搜索,那么沒(méi)問(wèn)題,可以用上索引;但是如果我們按照 age 進(jìn)行搜索,很明顯,age 在整個(gè)索引樹(shù)中是無(wú)序的,所以當(dāng)我們使用 age 作為搜索條件的時(shí)候,是沒(méi)法使用上圖這個(gè)聯(lián)合索引的。

  • 前綴匹配

如果我們搜索的關(guān)鍵字只是 username 字段的前半部分,那么很明顯,也是可以使用索引的,例如搜索所有以 a 開(kāi)始的 username。

  • 范圍匹配

如果我們的搜索條件是一個(gè)范圍,很明顯也可以使用到上述索引,例如搜索姓名介于 ab~cc 之間的用戶,只需要先從索引樹(shù)的根節(jié)點(diǎn)開(kāi)始,先找到 ab,然后根據(jù)葉子節(jié)點(diǎn)之間的指針順藤摸瓜,找到 cc 之后的第一個(gè)數(shù)據(jù)(不滿足條件的第一個(gè)數(shù)據(jù))結(jié)束。

  • 前面全值匹配,后面范圍匹配

例如查找 username 為 bw 且 age 介于 90~99 之間的用戶,這種情況也可以使用到上圖的索引。在上圖索引樹(shù)中,當(dāng) username 相同的時(shí)候,就是按照 age 排序的,所以對(duì)于 username 都為 bw 的用戶,它就是按照 age 進(jìn)行排序的,此時(shí),我們當(dāng)然可以按照 age 的范圍進(jìn)行搜索了。

  • 覆蓋索引

有的時(shí)候,我們搜索的數(shù)據(jù)都在索引樹(shù)中了,例如上圖中的索引,我們想搜索 username 為 bw 的用戶的 age,由于 age 就在索引樹(shù)中,直接返回即可,這就是覆蓋索引了。

2.4 使用限制

毫無(wú)疑問(wèn),基于 B+Tree 的索引,其實(shí)也存在一些使用限制。例如:

  1. 如果我們將 age 作為搜索條件,雖然 age 也是聯(lián)合索引的一部分,但是 age 整體上在索引樹(shù)中是無(wú)序的,所以將 age 作為搜索條件是沒(méi)法使用上述索引的。
  2. 基于第一點(diǎn),如果聯(lián)合索引中還有第三、第四列等,那么凡是跳過(guò)第一列直接使用后面的列作為查詢條件,索引都是不會(huì)生效的。
  3. 范圍條件的右邊無(wú)法使用索引直接定位。例如搜索 username 以 a 開(kāi)頭并且年齡為 99 的用戶:where username like 'a%' and age=99,此時(shí) age=99 這個(gè)條件就無(wú)法在索引樹(shù)中直接處理了(可以通過(guò)索引下推過(guò)濾)。原因很簡(jiǎn)單,當(dāng)我們找到所有 username 以 a 開(kāi)始的用戶之后,這些用戶的 age 并不是有序的,所以 age 就沒(méi)法繼續(xù)使用索引搜索了(但是可以通過(guò)索引下推過(guò)濾)。

關(guān)于第三點(diǎn),我舉一個(gè)例子,假設(shè)我們還有兩個(gè)用戶,分別是:

  • username 為 ad 且 age 為 80;
  • username 為 ae 且 age 為 88;

那么我們完善一下上面 B+Tree 的圖應(yīng)該變成下面這樣:

圖片

可以看到,username 以 a 開(kāi)始的用戶,age 并不是有序的,所以就只能通過(guò)索引下推過(guò)濾了,而無(wú)法直接通過(guò)索引掃描定位數(shù)據(jù)。

對(duì)于第三點(diǎn),如果范圍搜索的字段值的可能性比較少,則可以通過(guò)多個(gè)等于比較來(lái)代替范圍搜索。

2.5 自適應(yīng)哈希索引

Hash 索引在 MySQL 中主要是 Memory 和 NDB 引擎支持,InnoDB 索引本身是 不支持的,但是 InnoDB 索引有一個(gè)特性叫做自適應(yīng)哈希索引,自適應(yīng)三個(gè)字意味著整個(gè)過(guò)程是全自動(dòng)的,不需要開(kāi)發(fā)者配置。

當(dāng) InnoDB 監(jiān)控到某些索引值被頻繁的訪問(wèn)時(shí),那么它就會(huì)在 B+Tree 索引之上,構(gòu)建一個(gè) Hash 索引,進(jìn)而通過(guò) Hash 查找來(lái)快速訪問(wèn)數(shù)據(jù)。

默認(rèn)情況下,自適應(yīng)哈希索引是開(kāi)啟的狀態(tài),通過(guò)如下 SQL 我們可以查看:

圖片

可以看到,這個(gè)默認(rèn)就是開(kāi)啟的。

3. 小結(jié)

整體上來(lái)說(shuō),使用索引有如下優(yōu)點(diǎn):

  • 減少了服務(wù)器需要掃描的數(shù)據(jù)量。
  • 索引可以幫助服務(wù)器避免排序和創(chuàng)建臨時(shí)表。
  • 索引將隨機(jī) IO 變?yōu)榱隧樞?IO。
責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2021-10-12 07:58:10

MySQL索引數(shù)據(jù)

2011-07-11 15:03:36

MySQL索引數(shù)據(jù)結(jié)構(gòu)

2011-07-11 16:05:42

MySQL索引

2023-12-28 10:54:58

MySQL記錄存儲(chǔ)

2023-04-28 08:53:09

2020-08-12 08:30:20

數(shù)據(jù)結(jié)構(gòu)算法

2011-07-11 13:11:54

MySQL索引數(shù)據(jù)結(jié)構(gòu)

2017-05-11 11:59:12

MySQL數(shù)據(jù)結(jié)構(gòu)算法原理

2023-10-31 08:51:25

數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)

2011-03-31 15:41:51

Cacti數(shù)據(jù)表結(jié)構(gòu)

2012-04-28 14:21:47

Java數(shù)據(jù)結(jié)構(gòu)線性結(jié)構(gòu)

2023-06-08 07:25:56

數(shù)據(jù)庫(kù)索引數(shù)據(jù)結(jié)構(gòu)

2021-05-12 14:09:35

鏈表數(shù)據(jù)結(jié)構(gòu)線性結(jié)構(gòu)

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2021-08-03 10:24:59

數(shù)據(jù)跳躍鏈表結(jié)構(gòu)

2023-11-12 21:49:10

Redis數(shù)據(jù)庫(kù)

2022-11-04 08:29:05

索引數(shù)據(jù)庫(kù)JSON

2021-07-16 07:57:34

Python數(shù)據(jù)結(jié)構(gòu)

2023-07-03 17:24:33

數(shù)據(jù)結(jié)構(gòu)

2014-12-10 10:35:43

微信 數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲天堂免费在线 | 中文二区 | 羞羞在线视频 | 国产美女福利在线观看 | 亚洲视频一区二区三区 | 亚洲欧洲在线观看视频 | 最新日韩av| 天天干天天操 | 91免费入口 | 天天操伊人 | www.久久.com | 精品国产乱码久久久久久丨区2区 | 玖玖国产精品视频 | 国产成人高清 | 亚洲精品成人在线 | 一区二区三区在线播放 | 亚洲成人网在线观看 | 一级黄色生活视频 | 国产97色| 自拍偷拍第一页 | 久久精品中文字幕 | 久久久久国产一区二区三区 | 国产精品精品视频一区二区三区 | 亚洲视频中文字幕 | 嫩草伊人 | 成人av播放 | 婷婷久久五月天 | 亚洲精品久久久蜜桃 | 日本午夜网站 | 成人在线视频一区 | 一区二区在线 | 在线观看国产视频 | 国产999精品久久久影片官网 | 亚洲日本一区二区 | 日本理论片好看理论片 | 亚洲3p| 中文字幕综合在线 | 久久精品中文字幕 | 国产精品久久久久久久久久久久午夜片 | 欧美成人精品一区二区三区 | 日本特黄特色aaa大片免费 |