日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

储存引擎InnoDB 索引选择 为何是B+树 而不是 B树 哈希表

發(fā)布時(shí)間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 储存引擎InnoDB 索引选择 为何是B+树 而不是 B树 哈希表 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一:概述

首先需要澄清的一點(diǎn)是,MySQL 跟 B+ 樹沒(méi)有直接的關(guān)系,真正與 B+ 樹有關(guān)系的是 MySQL 的默認(rèn)存儲(chǔ)引擎 InnoDB,MySQL 中存儲(chǔ)引擎的主要作用是負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取,除了 InnoDB 之外,MySQL 中也支持 MyISAM 作為表的底層存儲(chǔ)引擎。

我們?cè)谑褂?SQL 語(yǔ)句創(chuàng)建表時(shí)就可以為當(dāng)前表指定使用的存儲(chǔ)引擎,你能在 MySQL 的文檔 Alternative Storage Engines 中找到它支持的全部存儲(chǔ)引擎,例如:MyISAM、CSV、MEMORY 等,然而默認(rèn)情況下,使用如下所示的 SQL 語(yǔ)句來(lái)創(chuàng)建表就會(huì)得到 InnoDB 存儲(chǔ)引擎支撐的表:

CREATE TABLE t1 (a INT,b CHAR (20 ), PRIMARY KEY (a)) ENGINE=InnoDB;

我們今天最終將要分析的問(wèn)題其實(shí)還是,為什么 MySQL 默認(rèn)的存儲(chǔ)引擎 InnoDB 會(huì)使用 MySQL 來(lái)存儲(chǔ)數(shù)據(jù),相信對(duì) MySQL 稍微有些了解的人都知道。

無(wú)論是表中的數(shù)據(jù)(主鍵索引)還是輔助索引最終都會(huì)使用 B+ 樹來(lái)存儲(chǔ)數(shù)據(jù),其中前者在表中會(huì)以 <id, row> 的方式存儲(chǔ),而后者會(huì)以 <index, id> 的方式進(jìn)行存儲(chǔ),這其實(shí)也比較好理解:

在主鍵索引中,id 是主鍵,我們能夠通過(guò) id 找到該行的全部列;
在輔助索引中,索引中的幾個(gè)列構(gòu)成了鍵,我們能夠通過(guò)索引中的列找到id,如果有需要的話,可以再通過(guò) id 找到當(dāng)前數(shù)據(jù)行的全部?jī)?nèi)容;
對(duì)于 InnoDB 來(lái)說(shuō),所有的數(shù)據(jù)都是以鍵值對(duì)的方式存儲(chǔ)的,主鍵索引和輔助索引在存儲(chǔ)數(shù)據(jù)時(shí)會(huì)將 id 和 index 作為鍵,將所有列和 id 作為鍵對(duì)應(yīng)的值。

在具體分析 InnoDB 使用 B+ 樹背后的原因之前,我們需要為 B+ 樹找?guī)讉€(gè)『假想敵』,因?yàn)槿绻覀冎挥幸粋€(gè)選擇,那么選擇 B+ 樹也并不值得討論,找到的兩個(gè)假想敵就是 B 樹和哈希,相信這也是很多人會(huì)在面試中真實(shí)遇到的問(wèn)題,我們就以這兩種數(shù)據(jù)結(jié)構(gòu)為例,分析比較 B+ 樹的優(yōu)點(diǎn)。

二:設(shè)計(jì)

到了這里我們已經(jīng)明確了今天待討論的問(wèn)題,也就是為什么 MySQL 的 InnoDB 存儲(chǔ)引擎會(huì)選擇 B+ 樹作為底層的數(shù)據(jù)結(jié)構(gòu),而不選擇 B 樹或者哈希?在這一節(jié)中,我們將通過(guò)以下的兩個(gè)方面介紹 InnoDB 這樣選擇的原因。

InnoDB 需要支持的場(chǎng)景和功能需要在特定查詢上擁有較強(qiáng)的性能;
CPU 將磁盤上的數(shù)據(jù)加載到內(nèi)存中需要花費(fèi)大量的時(shí)間,這使得 B+ 樹成為了非常好的選擇;
數(shù)據(jù)的持久化以及持久化數(shù)據(jù)的查詢其實(shí)是一個(gè)常見的需求,而數(shù)據(jù)的持久化就需要我們與磁盤、內(nèi)存和 CPU 打交道;MySQL 作為 OLTP 的數(shù)據(jù)庫(kù)不僅需要具備事務(wù)的處理能力,而且要保證數(shù)據(jù)的持久化并且能夠有一定的實(shí)時(shí)數(shù)據(jù)查詢能力,這些需求共同決定了 B+ 樹的選擇,接下來(lái)我們會(huì)詳細(xì)分析上述兩個(gè)原因背后的邏輯。

三:讀寫性能

很多人對(duì) OLTP 這個(gè)詞可能不是特別了解,我們幫助各位讀者快速理解一下,與 OLTP 相比的還有 OLAP,它們分別是 Online Transaction Processing 和 Online Analytical Processing,從這兩個(gè)名字中我們就可以看出,前者指的就是傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù),主要用于處理基本的、日常的事務(wù)處理,而后者主要在數(shù)據(jù)倉(cāng)庫(kù)中使用,用于支持一些復(fù)雜的分析和決策。

作為支撐 OLTP 數(shù)據(jù)庫(kù)的存儲(chǔ)引擎,我們經(jīng)常會(huì)使用 InnoDB 完成以下的一些工作:

通過(guò) INSERT、UPDATE 和 DELETE 語(yǔ)句對(duì)表中的數(shù)據(jù)進(jìn)行增加、修改和刪除;
通過(guò) UPDATE 和 DELETE 語(yǔ)句對(duì)符合條件的數(shù)據(jù)進(jìn)行批量的刪除;
通過(guò) SELECT 語(yǔ)句和主鍵查詢某條記錄的全部列;
通過(guò) SELECT 語(yǔ)句在表中查詢符合某些條件的記錄并根據(jù)某些字段排序;
通過(guò) SELECT 語(yǔ)句查詢表中數(shù)據(jù)的行數(shù);
通過(guò)唯一索引保證表中某個(gè)字段或者某幾個(gè)字段的唯一性;
如果我們使用 B+ 樹作為底層的數(shù)據(jù)結(jié)構(gòu),那么所有只會(huì)訪問(wèn)或者修改一條數(shù)據(jù)的 SQL 的時(shí)間復(fù)雜度都是 O(log n),也就是樹的高度,但是使用哈希卻有可能達(dá)到 O(1) 的時(shí)間復(fù)雜度,看起來(lái)是不是特別的美好。但是當(dāng)我們使用如下所示的 SQL 時(shí),哈希的表現(xiàn)就不會(huì)這么好了:

SELECT * FROM posts WHERE author = 'draven' ORDER BY created_at DESC SELECT * FROM posts WHERE comments_count > 10 UPDATE posts SET github = 'github.com/draveness' WHERE author = 'draven' DELETE FROM posts WHERE author = 'draven'

如果我們使用哈希作為底層的數(shù)據(jù)結(jié)構(gòu),遇到上述的場(chǎng)景時(shí),使用哈希構(gòu)成的主鍵索引或者輔助索引可能就沒(méi)有辦法快速處理了,它對(duì)于處理范圍查詢或者排序性能會(huì)非常差,只能進(jìn)行全表掃描并依次判斷是否滿足條件。

全表掃描對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō)是一個(gè)非常糟糕的結(jié)果,這其實(shí)也就意味著我們使用的數(shù)據(jù)結(jié)構(gòu)對(duì)于這些查詢沒(méi)有其他任何效果,最終的性能可能都不如從日志中順序進(jìn)行匹配。

使用 B+ 樹其實(shí)能夠保證數(shù)據(jù)按照鍵的順序進(jìn)行存儲(chǔ),也就是相鄰的所有數(shù)據(jù)其實(shí)都是按照自然順序排列的,使用哈希卻無(wú)法達(dá)到這樣的效果,因?yàn)楣:瘮?shù)的目的就是讓數(shù)據(jù)盡可能被分散到不同的桶中進(jìn)行存儲(chǔ),所以在遇到可能存在相同鍵 author = 'draven 或者排序以及范圍查詢 comments_count > 10 時(shí),由哈希作為底層數(shù)據(jù)結(jié)構(gòu)的表可能就會(huì)面對(duì)數(shù)據(jù)庫(kù)查詢的噩夢(mèng) —— 全表掃描。

B 樹和 B+ 樹在數(shù)據(jù)結(jié)構(gòu)上其實(shí)有一些類似,它們都可以按照某些順序?qū)λ饕械膬?nèi)容進(jìn)行遍歷,對(duì)于排序和范圍查詢等操作,B 樹和 B+ 樹相比于哈希會(huì)帶來(lái)更好的性能,當(dāng)然如果索引建立不夠好或者 SQL 查詢非常復(fù)雜,依然會(huì)導(dǎo)致全表掃描。

與 B 樹和 B+ 樹相比,哈希作為底層的數(shù)據(jù)結(jié)構(gòu)的表能夠以 O(1) 的速度處理單個(gè)數(shù)據(jù)行的增刪改查,但是面對(duì)范圍查詢或者排序時(shí)就會(huì)導(dǎo)致全表掃描的結(jié)果,而 B 樹和 B+ 樹雖然在單數(shù)據(jù)行的增刪查改上需要 O(log n) 的時(shí)間,但是它會(huì)將索引列相近的數(shù)據(jù)按順序存儲(chǔ),所以能夠避免全表掃描。

四:數(shù)據(jù)加載

既然使用哈希無(wú)法應(yīng)對(duì)我們常見的 SQL 中排序和范圍查詢等操作,而 B 樹和 B 樹和 B+ 樹都可以相對(duì)高效地執(zhí)行這些查詢,那么為什么我們不選擇 B 樹呢?這個(gè)原因其實(shí)非常簡(jiǎn)單 —— 計(jì)算機(jī)在讀寫文件時(shí)會(huì)以頁(yè)為單位將數(shù)據(jù)加載到內(nèi)存中。頁(yè)的大小可能會(huì)根據(jù)操作系統(tǒng)的不同而發(fā)生變化,不過(guò)在大多數(shù)的操作系統(tǒng)中,頁(yè)的大小都是 4KB,你可以通過(guò)如下的命令獲取操作系統(tǒng)上的頁(yè)大小:

$ getconf PAGE_SIZE
4096
當(dāng)我們需要在數(shù)據(jù)庫(kù)中查詢數(shù)據(jù)時(shí),CPU 會(huì)發(fā)現(xiàn)當(dāng)前數(shù)據(jù)位于磁盤而不是內(nèi)存中,這時(shí)就會(huì)觸發(fā) I/O 操作將數(shù)據(jù)加載到內(nèi)存中進(jìn)行訪問(wèn),數(shù)據(jù)的加載都是以頁(yè)的維度進(jìn)行加載的,然而將數(shù)據(jù)從磁盤讀取到內(nèi)存中所需要的成本是非常大的,普通磁盤(非 SSD)加載數(shù)據(jù)需要經(jīng)過(guò)隊(duì)列、尋道、旋轉(zhuǎn)以及傳輸?shù)倪@些過(guò)程,大概要花費(fèi) 10ms 左右的時(shí)間。


我們?cè)诠浪?MySQL 的查詢時(shí)就可以使用 10ms 這個(gè)數(shù)量級(jí)對(duì)隨機(jī) I/O 占用的時(shí)間進(jìn)行估算,這里想要說(shuō)的是隨機(jī) I/O 對(duì)于 MySQL 的查詢性能影響會(huì)非常大,而順序讀取磁盤中的數(shù)據(jù)時(shí)速度可以達(dá)到 40MB/s,這兩者的性能差距有幾個(gè)數(shù)量級(jí),由此我們也應(yīng)該盡量減少隨機(jī) I/O 的次數(shù),這樣才能提高性能。

B 樹與 B+ 樹的最大區(qū)別就是,B 樹可以在非葉結(jié)點(diǎn)中存儲(chǔ)數(shù)據(jù),但是 B+ 樹的所有數(shù)據(jù)其實(shí)都存儲(chǔ)在葉子節(jié)點(diǎn)中,當(dāng)一個(gè)表底層的數(shù)據(jù)結(jié)構(gòu)是 B 樹時(shí),假設(shè)我們需要訪問(wèn)所有『大于 4,并且小于 9 的數(shù)據(jù)』:

如果不考慮任何優(yōu)化,在上面的簡(jiǎn)單 B 樹中我們需要進(jìn)行 4 次磁盤的隨機(jī) I/O 才能找到所有滿足條件的數(shù)據(jù)行:

加載根節(jié)點(diǎn)所在的頁(yè),發(fā)現(xiàn)根節(jié)點(diǎn)的第一個(gè)元素是 6,大于 4;
通過(guò)根節(jié)點(diǎn)的指針加載左子節(jié)點(diǎn)所在的頁(yè),遍歷頁(yè)面中的數(shù)據(jù),找到 5;
重新加載根節(jié)點(diǎn)所在的頁(yè),發(fā)現(xiàn)根節(jié)點(diǎn)不包含第二個(gè)元素;
通過(guò)根節(jié)點(diǎn)的指針加載右子節(jié)點(diǎn)所在的頁(yè),遍歷頁(yè)面中的數(shù)據(jù),找到 7 和 8;
當(dāng)然我們可以通過(guò)各種方式來(lái)對(duì)上述的過(guò)程進(jìn)行優(yōu)化,不過(guò) B 樹能做的優(yōu)化 B+ 樹基本都可以,所以我們不需要考慮優(yōu)化 B 樹而帶來(lái)的收益,直接來(lái)看看什么樣的優(yōu)化 B+ 樹可以做,而 B 樹不行。

由于所有的節(jié)點(diǎn)都可能包含目標(biāo)數(shù)據(jù),我們總是要從根節(jié)點(diǎn)向下遍歷子樹查找滿足條件的數(shù)據(jù)行,這個(gè)特點(diǎn)帶來(lái)了大量的隨機(jī) I/O,也是 B 樹最大的性能問(wèn)題。

B+ 樹中就不存在這個(gè)問(wèn)題了,因?yàn)樗械臄?shù)據(jù)行都存儲(chǔ)在葉節(jié)點(diǎn)中,而這些葉節(jié)點(diǎn)可以通過(guò)『指針』依次按順序連接,當(dāng)我們?cè)谌缦滤镜?B+ 樹遍歷數(shù)據(jù)時(shí)可以直接在多個(gè)子節(jié)點(diǎn)之間進(jìn)行跳轉(zhuǎn),這樣能夠節(jié)省大量的磁盤 I/O 時(shí)間,也不需要在不同層級(jí)的節(jié)點(diǎn)之間對(duì)數(shù)據(jù)進(jìn)行拼接和排序;通過(guò)一個(gè) B+ 樹最左側(cè)的葉子節(jié)點(diǎn),我們可以像鏈表一樣遍歷整個(gè)樹中的全部數(shù)據(jù),我們也可以引入雙向鏈表保證倒序遍歷時(shí)的性能

有些讀者可能會(huì)認(rèn)為使用 B+ 樹這種數(shù)據(jù)結(jié)構(gòu)會(huì)增加樹的高度從而增加整體的耗時(shí),然而高度為 3 的 B+ 樹就能夠存儲(chǔ)千萬(wàn)級(jí)別的數(shù)據(jù),實(shí)踐中 B+ 樹的高度最多也就 4 或者 5,所以這并不是影響性能的根本問(wèn)題。

五:總結(jié)

任何不考慮應(yīng)用場(chǎng)景的設(shè)計(jì)都不是最好的設(shè)計(jì),當(dāng)我們明確的定義了使用 MySQL 時(shí)的常見查詢需求并理解場(chǎng)景之后,再對(duì)不同的數(shù)據(jù)結(jié)構(gòu)進(jìn)行選擇就成了理所當(dāng)然的事情,當(dāng)然 B+ 樹可能無(wú)法對(duì)所有 OLTP 場(chǎng)景下的查詢都有著較好的性能,但是它能夠解決大多數(shù)的問(wèn)題。

我們?cè)谶@里重新回顧一下 MySQL 默認(rèn)的存儲(chǔ)引擎選擇 B+ 樹而不是哈希或者 B 樹的原因:

哈希雖然能夠提供 O(1) 的單數(shù)據(jù)行操作性能,但是對(duì)于范圍查詢和排序卻無(wú)法很好地支持,最終導(dǎo)致全表掃描;
B 樹能夠在非葉節(jié)點(diǎn)中存儲(chǔ)數(shù)據(jù),但是這也導(dǎo)致在查詢連續(xù)數(shù)據(jù)時(shí)可能會(huì)帶來(lái)更多的隨機(jī) I/O,而 B+ 樹的所有葉節(jié)點(diǎn)可以通過(guò)指針相互連接,能夠減少順序遍歷時(shí)產(chǎn)生的額外隨機(jī) I/O;
如果想要追求各方面的極致性能也不是沒(méi)有可能,只是會(huì)帶來(lái)更高的復(fù)雜度,我們可以為一張表同時(shí)建 B+ 樹和哈希構(gòu)成的存儲(chǔ)結(jié)構(gòu),這樣不同類型的查詢就可以選擇相對(duì)更快的數(shù)據(jù)結(jié)構(gòu),但是會(huì)導(dǎo)致更新和刪除時(shí)需要操作多份數(shù)據(jù)。

從今天的角度來(lái)看,B+ 樹可能不是 InnoDB 的最優(yōu)選擇,但是它一定是能夠滿足當(dāng)時(shí)設(shè)計(jì)場(chǎng)景的需要,從 B+ 樹作為數(shù)據(jù)庫(kù)底層的存儲(chǔ)結(jié)構(gòu)到今天已經(jīng)過(guò)了幾十年的時(shí)間,我們不得不說(shuō)優(yōu)秀的工程設(shè)計(jì)確實(shí)有足夠的生命力。而我們作為工程師,在選擇數(shù)據(jù)庫(kù)時(shí)也應(yīng)該非常清楚地知道不同數(shù)據(jù)庫(kù)適合的場(chǎng)景,因?yàn)檐浖こ讨袥](méi)有銀彈。

寫的灰常灰常的nice!!!!!!
參考自:

總結(jié)

以上是生活随笔為你收集整理的储存引擎InnoDB 索引选择 为何是B+树 而不是 B树 哈希表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。