日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

上一次吊打各種樹這篇文章 堂主檸檬帶大家學習一遍數據結構中的各種樹,對數據結構還不夠熟悉的同學,那篇文章可以作為基礎入門,我畫了很多圖理解起來不困難,建議回頭先學習下那篇文章,更容易理解本文要講的內容。

文章里有提到B+樹被廣泛應用于MySQL數據庫的索引實現,不過并未展開細說,但是呢B+樹是一種重要的數據結構,常年出現在各種面試題中,這次就來一起學習下和B+樹相關的MySQL索引底層實現的內容。

面試官:簡單講講MySQL數據庫的索引實現,以及為什么這么實現?

這個面試題出現的頻率非常之高,從我自己和朋友們參加的大小廠面試都有被問過這個問題,大部分人可能看過一些網上的博客能說出個一二三,如果面試官沒有細問還真能混過去,但是對于細節沒能真正理解的非常透徹。

所以今天堂主檸檬就來寫寫這個話題,讓你知其然也知其所以然。寫作目標是無論你是否學過數據結構,看完都能徹底搞懂這個問題,花5分鐘來跟著學一遍看看我有沒有做到吧。

首先需要明白,數據庫索引是在存儲引擎層實現,常見的存儲引擎有 2 種。

InnoDB 存儲引擎:

innoDB存儲引擎支持事務,其設計目標是面向在線事務處理的應用,行鎖設計、支持外鍵,默認度操作不會產生鎖,從MySLQ 5.5.7版本開始,InnoDB存儲引擎作為默認的存儲引擎存在于MySLQ中。

MyISAM 存儲引擎:

MyISAM存儲引擎不支持事務,表鎖設計,支持全文索引,主要面向離線事務處理的數據庫應用,在InnoDB引擎成為默認引擎之前,MyISAM存儲引擎一直霸占著默認存儲引擎的位置,直到他被InnoDB取代,這是個悲傷的故事。

存儲引擎不同,索引實現方式也不盡相同,因此,我們先約定本文講的索引都是InnoDB存儲引擎實現的B+樹索引

MySQL架構

索引由存儲引擎實現,那存儲引擎到底是個什么東西呢?

從我們平常使用的的角度來看,對MySQL的直觀感受是命令行的各種指令,或是一個數據庫管理工具比如SQLyog的界面點擊操作,堂主檸檬在剛接觸MySQL時就是用的SQLyon圖形界面操作,就是下面這個小海豚。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

MySQL可能是世界上最流行的開源數據庫引擎,但使用基于文本的工具和配置文件管理起來可能很困難。SQLyog提供了一個完整的圖形界面,即使對于初學者來說,使用MySQL的強大功能也很簡單,SQLyog直觀的圖形用戶界面使您可以輕松管理MySQL數據庫的各個方面。

不管是使用圖形界面還是命令行,我們接觸到的都只是客戶端,MySQL作為一個軟件系統的架構,存儲引擎在MySQL服務端系統架構的什么位置呢?

總的來說,MySLQ系統架構分為 3 層,直接上圖,看下MySQL的架構劃分。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

  • 第一層:連接管理層。MySLQ是典型的CS模型軟件,所謂CS就是客戶端/服務端的意思,作為一個靠網絡連接的服務,必不可少的要有連接管理層,用于管理和維護MySQL服務端和客戶端之間的連接、鑒權等等。
  • 第二層:這一層是MySQL的核心服務功能層,包括了查詢緩存、解析器、優化器等所有跨存儲引擎的功能都在這一層實現,屏蔽掉存儲引擎間的差別,對上層也就是連接管理層提供統一的接口。
  • 第三層:存儲引擎層就在這一層實現,負責MySQL中數據的存儲和提取,這其中有我們今天的主角InnoDB存儲引擎和它實現的B+樹索引。

如何指定存儲引擎類型

既然要研究innoDB的索引,那我們首先來創建一個使用innoDB存儲引擎的表。

MySQL目前支持的存儲引擎種類非常豐富,可以在連接MySQL客戶端,進入到命令行模式下,輸入如下命令查看當前版本MySQL提供的所有存儲引擎。

show engines;
數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

可以看到上圖中有包含MyISAM 和 InnoDB 這兩種常見引擎,關于這些存儲引擎的詳細介紹,可以參考MySQL的官方文檔,我放上鏈接:

https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html

好了,現在來創建數據表并指定innoDB存儲引擎。

舉個栗子:創建表一張大佬數據表 BigOld,表中第一個字段是大佬 id 標識,第二個字段是大佬名字 name,并指定數據庫使用的存儲引擎類型ENGINE=InnoDB ,下面這條建表語句創建并指定了一個使用 InnoDB 存儲引擎的數據庫表。

CREATE TABLE BigOld (
    id INT,
    name CHAR (32), 
    PRIMARY KEY (id)) ENGINE=InnoDB;

當然,如果你一開始創建的表使用的不是InnoDB引擎,那也沒關系,還可以創建表之后修改表的存儲引擎,像下面的這樣操作。

alert table BigOld engine = InnoDB

索引

好了,經過前面的操作,終于我們有了一個innoDB的數據表,現在我們可以來講講innoDB存儲引擎的索引,索引的作用當然是為了快速的存取MySQL數據庫的數據。

舉個栗子,如果把MySQL比作一個大型圖書館,其中的數據比作圖書館里的書

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

圖書館 unsplash.com

你去圖書館找書,圖書館那么大我們不可能一頭扎進去,一層層、一個個書架去找想要的書,這時候我們會在圖書管理系統中通過圖書編號(索引)查詢到圖書所在的樓層,到了指定的樓層在通過書架上的提示找到對應區域,最終在某個書架找到想要的書,這個圖書編號就起到索引的作用,幫助我們快速找到圖書(數據)。

存儲形式

InnoDB存儲引擎用B+樹實現索引,說到B+樹不得不提到他的兄弟B樹,這兩者的區別比較微妙,但查詢磁盤存儲數據的性能上卻相差很大,要知道為何MySQL InnoDB 選擇B+樹而不選擇B樹做索引,我們先來假設分別用這兩種數據結構實現索引對比一下。

B樹索引

拿來一本數據結構的教材,我們翻開B樹的定義。一棵M階的b樹是這么定義的:

定義任意非葉子結點最多只有M個兒子,且M>2;

根結點的兒子數為[2, M];

除根結點以外的非葉子結點的兒子數為[M/2, M],向上取整;

非葉子結點的關鍵字個數=兒子數-1;

所有葉子結點位于同一層;

k個關鍵字把節點拆成k+1段,分別指向k+1個兒子,同時滿足查找樹的大小關系。

看完你大概有點懵,不過沒關系,我們現是要對比它和B+樹在數據庫索引上的使用,不是要去手寫一個數據庫索引,只要抓住它主要的特點便于理解幫助我們理解索引原理即可,這里抓住B樹最主要的兩個特點:

  1. 非葉子節點只存放「索引」和指向子節點的「指針」。
  2. 葉子節點存放「索引」和「數據」,且葉子節點之間沒有關聯。

便于理解,假如在數據庫中使用B樹來做索引結構,我試著畫出這個B樹的索引結構圖,如下:

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

B+樹索引

看完了B樹索引實現,現在我們來用B+樹實現索引看看,一樣的隨便打開一本數據結構教材找到B+樹的定義,一顆M階的B+樹:

有n棵子樹的非葉子結點中含有n個關鍵字(b樹是n-1個),這些關鍵字不保存數據,只用來索引,所有數據都保存在葉子節點(b樹是每個關鍵字都保存數據)。

所有的葉子結點中包含了全部關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點本身依關鍵字的大小自小而大順序鏈接。

所有的非葉子結點可以看成是索引部分,結點中僅含其子樹中的最大(或最小)關鍵字。

通常在b+樹上有兩個頭指針,一個指向根結點,一個指向關鍵字最小的葉子結點。

同一個數字會在不同節點中重復出現,根節點的最大元素就是b+樹的最大元素。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

如果以前沒接觸過數據結構相關概念,看完估計還是不大明白,沒關系,那不是本文的重點,我們不去深究細枝末節。

我來幫你總結下,B+樹和B樹有很多相同的特點,但也有一些不同讓B+樹在查詢磁盤存儲的數據時有更好的性能(為什么?我們稍后講),最大的不同是下面兩個

  1. 「數據」只存放葉子節點上面的,非葉子節點存放「索引」和「指針」。
  2. 葉子節點按大小順序通過雙向鏈表連接起來,可以像遍歷鏈表一樣遍歷整棵B+樹。

innoDB的選擇

innoDB的索引選擇用B+樹來實現,B樹和B+樹非常相似,為什么用B+樹不用B樹做索引呢?這就要從數據庫的存儲方式說起。

性能瓶頸

innoDB索引以B+樹形式組織存儲,我們首先要明白B+樹的每個節點不是保存在內存而是放在屬于外部存儲的「磁盤頁」中

為什么呢?因為內存快是快,但是它貴啊!而且很多數據不是熱點數據,十天半個月都用不上,完全沒必要都放在內存里面,想想看如果淘寶會把那種萬億級別的交易訂單數據如果都存在內存中嗎,馬爸爸雖然有錢也不至于這么奢侈。

重點關注下這里所說的磁盤頁,的大小每個系統可能不一樣,就堂主所用的這個centos linux系統來說,通過下面的命令查看磁盤頁大小為 4096B 也就是 4KB

[lemon/test]$ getconf PAGE_SIZE
4096

這些磁盤數據頁如果是B+樹的葉子節點,那就保存著關鍵字和數據(就是我們用 INSERT 語句塞進數據庫的那些數據)。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

如果是非葉子節點那就保存著關鍵字和指針(指向子節點的指針),從上圖B+樹實現的索引示意圖也可以看到這兩種存儲形式的差別。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

內存VS外存

當我們在MySQL InnoDB中執行 select 查詢語句,這個查找數據的過程是這樣的

  • 沿著B+樹索引來查找一個給定關鍵字(或者范圍關鍵字)的所在的數據行。
  • 找到數據行所在的磁盤頁,把這個磁盤頁加載到內存中。
  • 在內存中進行查找(比如二分查找),最終得到目標數據行內容。

我們知道磁盤是外部存儲設備,那MySQL數據庫查找的時候將磁盤中數據讀入內存這個過程是非常緩慢的,對于機械硬盤具體來說,從磁盤加載數據需要經過尋道、旋轉以及傳輸的這些過程,時間花費至少是幾十毫秒,作為對比,DDR4內存尋址時間僅為6ns左右

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

機械硬盤

內存讀取速度是磁盤讀取速度的100萬倍!雖然現在固態硬盤的速度提升了很多,但和內存比起來還是有很大的差距,所以要盡量減少數據庫對磁盤數據頁的隨機IO操作

由于磁盤讀寫耗時的原因,在高并發系統中關系型數據庫MySQL會成為系統性能瓶頸。

在高并發服務中幾十毫秒的的耗時太久了,假設10ms處理一個請求,那1秒處理100個 QPS=100 對比秒殺這一類的場景這個吞吐量完全是不夠用的,這也是現在像redis這樣的高速緩存數據庫會站在前面,為MySQL擋一刀的原因,因為Redis都是內存操作,速度非常快!

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

Why B+樹?

為了更方便的說明B樹和B+樹兩種存儲結構的差異,我們來對比下兩種樹實現的索引上讀取數據的過程,i再來回答innoDB 引擎為什么選B+樹。

為方便對比,假設我們在B和B+樹中我們都要「查詢 1 < id < 6 之間」的所有數據行。

B樹索引

先來看下如果索引用B樹來實現,查找數據的流程圖:

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

在不考慮任何優化的前提下,圖中綠色虛線是在B樹索引上查找數據的遍歷軌跡,來拆解一下:

  1. 從索引樹根節點開始,加載磁盤頁 page1 找到第一個節點 key=1 數據行(1,小林)不符合。
  2. 繼續通過指針找到磁盤頁面page2,加載磁盤頁page2到內存,key=2 符合,讀取數據行(2, 石頭)
  3. 重新加載磁盤頁 page1 找到第二個節點 key=3符合,讀取數據行(3,軒轅)。
  4. 繼續通過指針加載磁盤頁 page4 到內存,key=4 符合,讀取數據行(4,小北)。
  5. 重新加載磁盤頁 page1 找到第三個節點 key=5 符合,讀取數據行(5,why神)。

數一數上面的5個步驟有幾個「加載磁盤頁」字眼,5個,還記得上面我們說的:**加載磁盤數據非常費時!**這種隨機大量的磁盤IO造成了B樹索引的的性能瓶頸,使其與innoDB索引無緣。

B+樹索引

再來看下現在innoDB的用B+樹的實現方案吧,同樣的查詢條件,還是畫出數據查找的遍歷軌跡:

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

在不考慮任何優化的前提下,圖中綠色虛線是在innoDB B+樹索引上查找數據的遍歷軌跡,同樣來拆解一下:

  1. 從索引樹根節點開始,加載磁盤頁 page1 找到第一個節點 key=1不符合,繼續往下搜索。
  2. 通過指針找到磁盤頁page2, 加載磁盤頁page2 到內存,其中存放了key=1、2的數據行,讀取符合條件數據行。
  3. 由于葉子節點間組成雙向鏈表,直接順著page2 加載磁盤頁page3 、 加載磁盤頁page4,讀取其中符合條件的數據行。

這其中涉及了4次加載磁盤頁的操作,看起來只是比上面的B樹少了一次,但這是在我的簡單例子,為了便于理解數據量比較少。

實際應用中B+確實能夠減少大量的磁盤IO,從而大大提高查詢性能,而且由于B+樹根節點的數據已經是排序好的雙向鏈表,我們可以像鏈表一樣遍歷所有數據,非常適合范圍查找和排序操作!

再談B樹

B樹索引并非一無是處。雖然我們前面詳細對比了在innoDB中使用B+樹索引的優勢,但不要以為B樹就一無是處了,一種數據結構被設計出來肯定是有使用場景的需求,不然誰也不會閑著沒事折騰出一個東西,你說對吧。

事實上B樹也被用于實現數據庫索引,只不過不是在MySQL上。在MongoDB的索引設計上就使用了B樹做索引,什么是MongoDB呢?我不做過多介紹,感興趣的可以下來了解一下,下面這段話來自MongoDB 英文Wiki

MongoDB is a cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with optional schemas. MongoDB is developed by MongoDB Inc. and licensed under the Server Side Public License (SSPL).

只需要知道它和MySQl不同,MongoDB是一種文檔型的非關系型數據庫,被劃分為NoSql數據庫,使用類似JSON的語法存儲文檔,熟悉Redis的同學應該很容易理解NoSQL的含義。

因為關系型數據庫比如 MySQL 經常需要執行遍歷和范圍查找的操作,B+樹的特點正是迎合了這樣的需求。但是在MongoDB這樣的NoSLQ數據庫中,在數據表的設計層面就決定了不會有太多的遍歷和范圍查找,基本就是一個鍵對應一個值的單一查詢

在MongoDB上的查找如果用B+樹來實現索引的話,由于非葉子節點不存放數據,每次查詢必須搜索到B+樹的葉子節點才能獲取到數據,時間復雜度是固定的樹的高度 log n;

而如果用B樹實現索引,由于每個節點都可以存放數據,幸運的話有可能不必找到葉子節點就能取得數據,復雜度更低,再來看下B樹實現的索引,如果換作是 MongoDB 你仔細品。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

雖然沒有MySQL的使用普及程度那么高,用B樹實現索引的MongoDB很多大公司也都有使用。

數據庫大揭秘:10張圖告訴你MySQL為什么選B+樹做索引?

 

使用客戶

脫離業務場景談具體實現都是耍流氓。正是由于關系型數據庫和非關系型數據庫應用場景的不同,工程實現上最終會在損失和收益中找到平衡點,使得B樹和B+樹在不同數據庫上有各自的用武之地。

所以下次面試和面試官夸MySQL B+樹索引好處的時候,注意別把 B 樹噴的太慘,以防面試官來個回馬槍,讓你說說為啥MongoDB還要用B樹來實現索引?這時候雖然你心里笑開了花,還是要假裝再思考下,愣著干嘛,接著繼續吹B樹啊。

感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反復求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,我們一起在探討中學習。


Hi,我是堂主檸檬,一線互聯網大廠后端程序員一枚,個人技術gzh主要分享后端開發相關的技術學習,每篇文章都是我精心創作,如果文章對你有幫助,這次一定不要白piao,點贊 或 分享 給需要的朋友,這對檸檬很重要,在此先謝過各位大佬了!我是檸檬,我們下期再見。

可以微信搜索公眾號「 后端技術學堂 」回復「1024」獲取50本計算機電子書,回復「進群」拉你進百人讀者技術百人群,文章每周持續更新,我們下期見!

分享到:
標簽:MySQL
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定