偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

全面透徹,深刻理解 MySQL 索引

數(shù)據(jù)庫 MySQL
MySQL 普遍采用 B+Tree 實(shí)現(xiàn),索引本身很大,不可能全部存儲(chǔ)內(nèi)存,因此需要以索引文件的形式存儲(chǔ)磁盤。

對(duì)于 MySQL 索引,相信每位后端同學(xué)日常工作中經(jīng)常會(huì)用到,但是對(duì)其索引原理,卻可能未曾真正深入了解。B- 樹和 B+ 樹是 MySQL 索引使用的數(shù)據(jù)結(jié)構(gòu),對(duì)于索引優(yōu)化和原理理解都非常重要,下面就揭開 B- 樹和 B+ 樹的神秘面紗,讓大家在面試的時(shí)候碰到這個(gè)知識(shí)點(diǎn)一往無前,不再成為你前進(jìn)的羈絆!

本文主要內(nèi)容:

  1. MySQL 查詢耗時(shí)分析,抓性能優(yōu)化核心問題點(diǎn)
  2. 常見用于查詢的數(shù)據(jù)結(jié)構(gòu),性能優(yōu)劣對(duì)比
  3. B-Tree 與 B+Tree 如何選擇,結(jié)合場景來做分析更靠譜
  4. InnoDB 中的索引介紹,知己知彼,應(yīng)用得心應(yīng)手

一、查詢耗時(shí)分析

我們首先進(jìn)行下查詢耗時(shí)分析,抓性能優(yōu)化核心問題點(diǎn)。

1.1 記錄讀取順序

MYSQL維護(hù)著自己的緩存空間,如果要讀取一條記錄,根據(jù)優(yōu)先順序,路徑如下:

緩存區(qū) => 磁盤緩存區(qū) => 磁盤

1.1.1 緩存區(qū)讀取

這是成本最低的方式,可以直接被CPU使用,不涉及磁盤IO,可以考慮IO時(shí)間為0。

1.1.2 從磁盤緩存區(qū)讀取

如果磁盤緩存區(qū)有需要的記錄,則只需要直接讀出,傳輸時(shí)間考慮為1ms。

1.1.3 從磁盤讀取

由于SSD比較貴,常用的還是機(jī)械硬盤,對(duì)于機(jī)械硬盤,要讀取指定地址的數(shù)據(jù),是需要經(jīng)過尋道的,機(jī)械臂需要先移動(dòng)到指定位置,因此無論讀取多少數(shù)據(jù),準(zhǔn)備工作都會(huì)耗費(fèi)一段時(shí)間。整個(gè)IO流程包括:排隊(duì)等待 => 尋道 => 半圈旋轉(zhuǎn) => 傳輸。

圖片圖片

一次隨機(jī)讀取中,有90%的時(shí)間都花費(fèi)在排隊(duì)和準(zhǔn)備工作,真正的傳輸時(shí)間只有1ms,但總I/O時(shí)間卻為10ms。

當(dāng)隨機(jī)讀取10頁,就需要 10*10=100ms,但如果是順序讀,對(duì)于傳輸速度為40MB/s的硬盤,讀取一個(gè)4kb的頁僅需要0.1ms,即使順序讀取100頁,也只需要1頁隨機(jī)讀99頁順序讀,也就是10ms+9.9ms=19.9ms。

速度差距幾十倍,這也是為何我們想要盡量保證需要讀取的數(shù)據(jù)都在物理上排列在一起,因?yàn)檫@樣就可以順序讀取多個(gè)頁,而不需要進(jìn)行多次隨機(jī)讀取。

這樣做的理論依據(jù)是計(jì)算機(jī)科學(xué)中著名的局部性原理:

當(dāng)一個(gè)數(shù)據(jù)被用到時(shí),其附近的數(shù)據(jù)也通常會(huì)馬上被使用。

通過以上分析可知:對(duì)于數(shù)據(jù)讀取速度的優(yōu)化,主要就是需要降低IO時(shí)間,而降低IO時(shí)間的關(guān)鍵,就在于減少隨機(jī)讀次數(shù)以及讀取更少的數(shù)據(jù)。

二、常見查詢數(shù)據(jù)結(jié)構(gòu)對(duì)比

在此梳理常見用于查詢的數(shù)據(jù)結(jié)構(gòu),性能優(yōu)劣大比拼。

2.1 二叉查找樹

二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。是數(shù)據(jù)結(jié)構(gòu)中的一類。在一般情況下,查詢效率比鏈表結(jié)構(gòu)要高。

如圖所示:

圖片圖片

查詢數(shù)據(jù)的效率不穩(wěn)定,若樹左右比較平衡的時(shí),最差情況為O(logN),如果插入數(shù)據(jù)是有序的,退化為了鏈表,查詢時(shí)間變成了O(N)。

數(shù)據(jù)量大的情況下,會(huì)導(dǎo)致樹的高度變高,如果每個(gè)節(jié)點(diǎn)對(duì)應(yīng)磁盤的一個(gè)塊來存儲(chǔ)一條數(shù)據(jù),需IO次數(shù)大幅增加,顯然用此結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)是不可取的。

2.2 平衡二叉樹(AVL樹)

平衡二叉樹(Balanced Binary Tree)指的是棵空樹或它的左右兩個(gè)子樹的高度差的絕對(duì)值不超過1,并且左右兩個(gè)子樹都是一棵平衡二叉樹。

如圖所示:

圖片圖片

如果數(shù)據(jù)都存儲(chǔ)在內(nèi)存中,采用AVL樹來存儲(chǔ),還是可以的,查詢效率非常高。不過我們的數(shù)據(jù)是存在磁盤中,用過采用這種結(jié)構(gòu),每個(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè)磁盤塊,數(shù)據(jù)量大的時(shí)候,也會(huì)和二叉樹一樣,會(huì)導(dǎo)致樹的高度變高,這樣邏輯上很近的節(jié)點(diǎn)實(shí)際可能非常遠(yuǎn),無法很好的利用磁盤預(yù)讀(局部性原理),會(huì)增加IO次數(shù),顯然用這種結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)也是不可取的。

2.3 B-樹

B樹(英語:B-tree),這里的 B 表示 balance( 平衡的意思),是一種自平衡的樹,能夠保持?jǐn)?shù)據(jù)有序,B-樹允許每個(gè)節(jié)點(diǎn)有更多的子節(jié)點(diǎn)。

如圖所示:

圖片圖片

B-樹不利于范圍查找,范圍查找也是我們經(jīng)常用到的,所以B-樹也不太適合在磁盤中存儲(chǔ)需要檢索的數(shù)據(jù)。

2.4 B+樹

B+樹是 B-樹的一個(gè)升級(jí)版,也是一種多路搜索樹,相對(duì)于 B 樹來說 B+樹更充分的利用了節(jié)點(diǎn)的空間,讓查詢速度更加穩(wěn)定,其速度完全接近于二分法查找。B+樹元素自底向上插入,這與二叉樹恰好相反。

如圖所示:

圖片圖片

如上圖所示,每個(gè)節(jié)點(diǎn)占用一個(gè)盤塊的磁盤空間,一個(gè)節(jié)點(diǎn)上有兩個(gè)升序排序的關(guān)鍵字和三個(gè)指向子樹根節(jié)點(diǎn)的指針,指針存儲(chǔ)的是子節(jié)點(diǎn)所在磁盤塊的地址。

2.5 B-Tree vs B+Tree

對(duì)于B-Tree和B+Tree,我們?cè)撊绾芜x擇呢?結(jié)合場景來做分析更靠譜。

讓我們來想想平時(shí)的高頻查詢場景吧,大概存在如下幾種:

  1. 按照id查詢唯一一條記錄 
  2. 查找某個(gè)范圍的所有記錄

接下來,just do it!

2.5.1 場景:按照id查詢唯一一條記錄 

圖片圖片

B-樹 模擬查找關(guān)鍵字20的過程(3次io操作+內(nèi)存中二分法)):

  1. 根據(jù)根節(jié)點(diǎn)找到磁盤塊1,讀入內(nèi)存?!敬疟PI/O操作第1次】
  2. 比較關(guān)鍵字20在區(qū)間(12,32),找到磁盤塊1的指針P2
  3. 根據(jù)P2指針找到磁盤塊3,讀入內(nèi)存?!敬疟PI/O操作第2次】
  4. 比較關(guān)鍵字20在區(qū)間(15,26),找到磁盤塊3的指針P2
  5. 根據(jù)P2指針找到磁盤塊7,讀入內(nèi)存?!敬疟PI/O操作第3次】
  6. 在磁盤塊7中的關(guān)鍵字列表中找到關(guān)鍵字20

如果我們是關(guān)鍵字 15 的話 ,那就是2次io操作+內(nèi)存中二分法。但是如果是B+樹,就只能通讀到底了。

2.5.2 場景:查找某個(gè)范圍的所有記錄

圖片圖片

B-樹要查找[8,52]區(qū)間的數(shù)據(jù),需要訪問8個(gè)磁盤塊(1/2/6/3/7/8/4/9),IO次數(shù)又上去了。

2.5.3 小結(jié)

故B-Tree vs B+Tree兩種在索引的區(qū)別如下:

1.B-Tree查找某個(gè)關(guān)鍵字的效率更高

B-Tree因?yàn)榉侨~子結(jié)點(diǎn)也保存具體數(shù)據(jù),所以在查找某個(gè)關(guān)鍵字的時(shí)候找到即可返回。而B+Tree所有的數(shù)據(jù)都在葉子結(jié)點(diǎn),每次查找都得到葉子結(jié)點(diǎn)。所以在同樣高度的B-Tree和B+Tree中,B-Tree查找某個(gè)關(guān)鍵字的效率更高。

2.B-Tree不利于范圍查詢

由于B+Tree所有的數(shù)據(jù)都在葉子結(jié)點(diǎn),并且結(jié)點(diǎn)之間有指針連接,在找大于某個(gè)關(guān)鍵字或者小于某個(gè)關(guān)鍵字的數(shù)據(jù)的時(shí)候,B+Tree只需要找到該關(guān)鍵字然后沿著鏈表遍歷就可以了,而B-Tree還需要遍歷該關(guān)鍵字結(jié)點(diǎn)的根結(jié)點(diǎn)去搜索。

3.B-Tree的深度會(huì)更大

由于B-Tree的每個(gè)結(jié)點(diǎn)(這里的結(jié)點(diǎn)可以理解為一個(gè)數(shù)據(jù)頁)都存儲(chǔ)主鍵+實(shí)際數(shù)據(jù),而B+Tree非葉子結(jié)點(diǎn)只存儲(chǔ)關(guān)鍵字信息,而每個(gè)頁的大小有限是有限的,所以同一頁能存儲(chǔ)的B-Tree的數(shù)據(jù)會(huì)比B+Tree存儲(chǔ)的更少。這樣同樣總量的數(shù)據(jù),B-Tree的深度會(huì)更大,增大查詢時(shí)的磁盤I/O次數(shù),進(jìn)而影響查詢效率。

4.B+樹的磁盤讀寫代價(jià)更低

B+樹的內(nèi)部節(jié)點(diǎn)并沒有指向關(guān)鍵字具體信息的指針,因此其內(nèi)部節(jié)點(diǎn)相對(duì)B樹更小,如果把所有同一內(nèi)部節(jié)點(diǎn)的關(guān)鍵字存放在同一盤塊中,那么盤塊所能容納的關(guān)鍵字?jǐn)?shù)量也越多,一次性讀入內(nèi)存的需要查找的關(guān)鍵字也就越多,相對(duì)IO讀寫次數(shù)就降低了。

5.B+樹的查詢效率更加穩(wěn)定

由于非終結(jié)點(diǎn)并不是最終指向文件內(nèi)容的結(jié)點(diǎn),而只是葉子結(jié)點(diǎn)中關(guān)鍵字的索引。所以任何關(guān)鍵字的查找必須走一條從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的路。所有關(guān)鍵字查詢的路徑長度相同,導(dǎo)致每一個(gè)數(shù)據(jù)的查詢效率相當(dāng)。

三、InnoDB中的索引

InnoDB 中的索引介紹,知己知彼,應(yīng)用起來得心應(yīng)手。

3.1 InnoDB索引實(shí)現(xiàn)

InnoDB數(shù)據(jù)頁有7個(gè)組成部分,各個(gè)數(shù)據(jù)頁可以組成一個(gè)雙向鏈表。而每個(gè)數(shù)據(jù)頁中的記錄會(huì)按照主鍵值從小到大的順序組成一個(gè)單向鏈表,每個(gè)數(shù)據(jù)頁都會(huì)為存儲(chǔ)在它里邊兒的記錄生成一個(gè)頁目錄。在通過主鍵查找某條記錄的時(shí)候可以在頁目錄中使用二分法快速定位到對(duì)應(yīng)的槽,然后再遍歷該槽對(duì)應(yīng)分組中的記錄即可快速找到指定的記錄。

頁和記錄的關(guān)系示意圖如下:

圖片圖片

索引同樣存儲(chǔ)在數(shù)據(jù)頁中,只不過目錄項(xiàng)中的兩個(gè)列是主鍵和頁號(hào)。

那InnoDB怎么區(qū)分一條記錄是普通的用戶記錄還是目錄項(xiàng)記錄呢?是根據(jù)記錄頭信息里的record_type屬性,它的各個(gè)取值代表的意思如下:

0:普通的用戶記錄

1:目錄項(xiàng)記錄

2:最小記錄

3:最大記錄

3.2 查找步驟

整體結(jié)構(gòu)如下:

圖片圖片

現(xiàn)在如果我們想根據(jù)主鍵值查找一條用戶記錄大致需要3個(gè)步驟,以查找主鍵值為20的記錄為例:

1、確定目錄項(xiàng)記錄頁。

2、通過目錄項(xiàng)記錄頁確定用戶記錄真實(shí)所在的頁。

3、在真實(shí)存儲(chǔ)用戶記錄的頁中定位到具體的記錄。

四、InnoDB中的索引分類

InnoDB 中的索引類別介紹,明確后期應(yīng)用注意事項(xiàng)。

4.1 聚簇索引

上邊介紹的B+樹索引。它有兩個(gè)特點(diǎn):

1.根據(jù)記錄主鍵值的大小進(jìn)行記錄和頁的排序

這包括三個(gè)方面的含義:

  • 頁內(nèi)的記錄是按照主鍵的大小順序排成一個(gè)單向鏈表。
  • 各個(gè)存放用戶記錄的頁也是根據(jù)主鍵大小順序排成一個(gè)雙向鏈表。
  • 存放目錄項(xiàng)記錄的頁分為不同的層次,在同一層次中的頁也是根據(jù)頁中目錄項(xiàng)記錄的主鍵大小順序排成一個(gè)雙向鏈表。

2.B+樹的葉子節(jié)點(diǎn)存儲(chǔ)的是完整的用戶記錄

我們把具有這兩種特性的B+樹稱為聚簇索引,所有完整的用戶記錄都存放在這個(gè)聚簇索引的葉子節(jié)點(diǎn)處。

4.2 二級(jí)索引

上邊介紹的聚簇索引只能在搜索條件是主鍵值時(shí)才能發(fā)揮作用,因?yàn)锽+樹中的數(shù)據(jù)都是按照主鍵進(jìn)行排序的。

那如果我們想以別的列作為搜索條件該咋辦呢?

難道只能從頭到尾沿著鏈表依次遍歷記錄么?

我們可以多建幾棵B+樹,不同的B+樹中的數(shù)據(jù)采用不同的排序規(guī)則。比方說我們用c2列的大小作為數(shù)據(jù)頁、頁中記錄的排序規(guī)則,再建一棵B+樹,效果如下圖所示:

圖片圖片

但是但是這個(gè)B+樹的葉子節(jié)點(diǎn)中的記錄只存儲(chǔ)了c2和c1(也就是主鍵)兩個(gè)列,所以我們必須再根據(jù)主鍵值去聚簇索引中再查找一遍完整的用戶記錄。

由于主鍵值具有唯一性,二級(jí)索引不具有唯一性,那么 新的問題來了:

圖片圖片

在上圖中,如果我們想新插入一行記錄,其中c1、c2、c3的值分別是:9、1、'c'。

那么在修改這個(gè)為c2列建立的二級(jí)索引對(duì)應(yīng)的B+樹時(shí)便碰到了個(gè)大問題:

由于頁3中存儲(chǔ)的目錄項(xiàng)記錄是由c2列 + 頁號(hào)的值構(gòu)成的,頁3中的兩條目錄項(xiàng)記錄對(duì)應(yīng)的c2列的值都是1,而我們新插入的這條記錄的c2列的值也是1,那我們這條新插入的記錄到底應(yīng)該放到頁4中,還是應(yīng)該放到頁5中?。裤卤屏?。

為了讓新插入記錄能找到自己在那個(gè)頁里,我們需要保證在B+樹的同一層內(nèi)節(jié)點(diǎn)的目錄項(xiàng)記錄除頁號(hào)這個(gè)字段以外是唯一的。

所以對(duì)于二級(jí)索引的內(nèi)節(jié)點(diǎn)的目錄項(xiàng)記錄的內(nèi)容實(shí)際上是由三個(gè)部分構(gòu)成的:1、索引列的值2、主鍵值3、頁號(hào)。

我們?yōu)閏2列建立二級(jí)索引后的示意圖實(shí)際上應(yīng)該是這樣子的:

圖片圖片

4.3 聯(lián)合索引

我們可以同時(shí)以多個(gè)列的大小作為排序規(guī)則,也就是同時(shí)為多個(gè)列建立索引,比方說我們想讓B+樹按照c2和c3列的大小進(jìn)行排序,這個(gè)包含兩層含義:

1.先把各個(gè)記錄和頁按照c2列進(jìn)行排序;

2.在記錄的c2列相同的情況下,采用c3列進(jìn)行排序。

五、總結(jié)

MySQL 普遍采用 B+Tree 實(shí)現(xiàn),索引本身很大,不可能全部存儲(chǔ)內(nèi)存,因此需要以索引文件的形式存儲(chǔ)磁盤。相對(duì)于內(nèi)存讀取,I/O 存取的消耗要高幾個(gè)數(shù)量級(jí),由于 MySQL 數(shù)據(jù)存儲(chǔ)保存在磁盤中,所以在查詢時(shí)磁盤 I/O 是其主要查詢性能瓶頸,而使用索引就可以減少磁盤 I/O。

索引的原理其實(shí)是不斷的縮小查找范圍, 就如我們平時(shí)用字典查單詞一樣,先找首字母縮小范圍,再第二個(gè)字母等等。

對(duì)于B-樹、B+樹而言,當(dāng)高固定情況下,寬度越大索引性能越好。

責(zé)任編輯:武曉燕 來源: 架構(gòu)精進(jìn)之路
相關(guān)推薦

2024-06-24 08:31:42

2017-01-13 08:52:46

HDFS機(jī)制Then

2011-04-18 19:36:10

HSRP協(xié)議

2012-12-31 14:59:58

Android開發(fā)Layout_weig

2011-05-18 09:47:39

spring

2011-03-14 13:11:07

Oracle數(shù)據(jù)庫

2020-09-20 22:14:14

編程PythonJava

2010-08-02 10:11:51

DB2數(shù)據(jù)庫編目

2012-06-21 10:00:25

團(tuán)隊(duì)合作程序員

2016-11-03 08:57:02

javascriptjquerynode.js

2022-12-04 09:19:25

JAVA并發(fā)有序性

2024-05-28 08:32:18

2020-06-29 08:32:21

高并發(fā)程序員流量

2015-05-27 15:27:53

KVMPowerKVM

2022-08-28 19:15:56

RabbitMQ性能優(yōu)化

2020-03-26 16:40:07

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

2020-03-17 08:36:22

數(shù)據(jù)庫存儲(chǔ)Mysql

2017-08-30 17:47:35

MySql索引

2015-09-24 08:43:13

程序員未來

2019-02-13 19:00:01

深度學(xué)習(xí)機(jī)器學(xué)習(xí)人工神經(jīng)
點(diǎn)贊
收藏

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