<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「干貨」MySQL 的 InnoDB 存儲引擎是怎么設(shè)計(jì)的?

          共 4711字,需瀏覽 10分鐘

           ·

          2020-02-02 23:21


          本文公眾號來源:柳樹的絮叨叨作者:靠發(fā)型吃飯的柳樹本文已收錄至我的GitHub

          上一講:MySQL 是如何實(shí)現(xiàn) ACID 中的 D 的?我用了一個問題,給大家介紹了 MySQL 中的兩個成員 binlog 和 redo log。然而,這只是 MySQL 家族里的兩個小嘍啰,Mysql 可以做到高性能高可靠,靠的絕對不只有他們倆。

          MySQL 里還有什么其他成員呢?

          對于 MySQL,要記住、或者要放在你隨時可以找到的地方的兩張圖,一張是 MySQL 架構(gòu)圖,另一張則是 InnoDB 架構(gòu)圖:

          266259d7a4eaba67729b6a17aab6b586.webp

          27ad3c0742f4901a0e3c7ad3a440c488.webp

          遇到問題,或者學(xué)習(xí)到新知識點(diǎn)時,就往里套,想一想,這是對應(yīng)這兩張圖的哪個模塊、是屬于具體哪個成員的能力。

          這其中,第一張圖的最底下的存儲引擎層(Storage Engines),它決定了 MySQL 會怎樣存儲數(shù)據(jù),怎樣讀取和寫入數(shù)據(jù),也在很大程度上決定了 MySQL 的讀寫性能和數(shù)據(jù)可靠性。

          對于這么重要的一層能力,MySQL 提供了極強(qiáng)的擴(kuò)展性,你可以定義自己要使用什么樣的存儲引擎:InnoDB、MyISAM、MEMORY、CSV,甚至可以自己開發(fā)一個存儲引擎然后使用它。

          我一直覺得 MySQL 的設(shè)計(jì),是教科書式的,高內(nèi)聚松耦合,邊界明確,職責(zé)清晰。學(xué)習(xí) MySQL,學(xué)的不只是如何更好的使用 MySQL,更是學(xué)習(xí)如何更好的進(jìn)行系統(tǒng)設(shè)計(jì)。

          通常我們說 Mysql 高性能高可靠,都是指基于 InnoDB 存儲引擎的 Mysql,所以,這一講,先讓我們來看看,除了 redo log,InnoDB 里還有哪些成員,他們都有什么能力,承擔(dān)了什么樣的角色,他們之間又是怎么配合的?

          InnoDB 內(nèi)存架構(gòu)

          從上面第二張圖可以看到,InnoDB 主要分為兩大塊:

          • InnoDB In-Memory Structures

          • InnoDB On-Disk Structures

          內(nèi)存和磁盤,讓我們先從內(nèi)存開始。

          1、Buffer Pool

          The buffer pool is an area in main memory where?InnoDB?caches table and index data as it is accessed.

          正如之前提到的,MySQL 不會直接去修改磁盤的數(shù)據(jù),因?yàn)檫@樣做太慢了,MySQL 會先改內(nèi)存,然后記錄 redo log,等有空了再刷磁盤,如果內(nèi)存里沒有數(shù)據(jù),就去磁盤 load。

          而這些數(shù)據(jù)存放的地方,就是 Buffer Pool。

          我們平時開發(fā)時,會用 redis 來做緩存,緩解數(shù)據(jù)庫壓力,其實(shí) MySQL 自己也做了一層類似緩存的東西。

          MySQL 是以「頁」(page)為單位從磁盤讀取數(shù)據(jù)的,Buffer Pool 里的數(shù)據(jù)也是如此,實(shí)際上,Buffer Pool 是a linked list of pages,一個以頁為元素的鏈表。

          為什么是鏈表?因?yàn)楹途彺嬉粯樱残枰惶滋蕴惴▉砉芾頂?shù)據(jù)。

          Buffer Pool 采用基于 LRU(least recently used) 的算法來管理內(nèi)存:

          94ea1b8685ce421e2561cb805030096a.webp

          關(guān)于 Buffer Pool 的更多知識,諸如如何配置大小、如何監(jiān)控等等:Buffer Pool

          2、Change Buffer

          上面提到過,如果內(nèi)存里沒有對應(yīng)「頁」的數(shù)據(jù),MySQL 就會去把數(shù)據(jù)從磁盤里 load 出來,如果每次需要的「頁」都不同,或者不是相鄰的「頁」,那么每次 MySQL 都要去 load,這樣就很慢了。

          于是如果 MySQL 發(fā)現(xiàn)你要修改的頁,不在內(nèi)存里,就把你要對頁的修改,先記到一個叫 Change Buffer 的地方,同時記錄 redo log,然后再慢慢把數(shù)據(jù) load 到內(nèi)存,load 過來后,再把 Change Buffer 里記錄的修改,應(yīng)用到內(nèi)存(Buffer Pool)中,這個動作叫做?merge;而把內(nèi)存數(shù)據(jù)刷到磁盤的動作,叫?purge

          • merge:Change Buffer -> Buffer Pool

          • purge:Buffer Pool -> Disk

          e65ccccb8c49bd1a9bc120a542cdf8bd.webp

          The change buffer is a special data structure that caches changes to?secondary index?pages when those pages are not in the?buffer pool. The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), are?merged?later when the pages are loaded into the buffer pool by other read operations.

          上面是 MySQL 官網(wǎng)對 Change Buffer 的定義,仔細(xì)看的話,你會發(fā)現(xiàn)里面提到:Change Buffer 只在操作「二級索引」(secondary index)時才使用,原因是「聚簇索引」(clustered indexes)必須是「唯一」的,也就意味著每次插入、更新,都需要檢查是否已經(jīng)有相同的字段存在,也就沒有必要使用 Change Buffer 了;另外,「聚簇索引」操作的隨機(jī)性比較小,通常在相鄰的「頁」進(jìn)行操作,比如使用了自增主鍵的「聚簇索引」,那么 insert 時就是遞增、有序的,不像「二級索引」,訪問非常隨機(jī)。

          如果想深入理解 Change Buffer 的原理,除了 MySQL 官網(wǎng)的介紹:Change Buffer,還可以閱讀下《MySQL技術(shù)內(nèi)幕》的「2.6.1 - 插入緩沖」章節(jié),里面會從 Change Buffer 的前身 —— Insert Buffer 開始講起,很透徹。

          3、Adaptive Hash Index

          MySQL 索引,不管是在磁盤里,還是被 load 到內(nèi)存后,都是 B+ 樹,B+ 樹的查找次數(shù)取決于樹的深度。你看,數(shù)據(jù)都已經(jīng)放到內(nèi)存了,還不能“一下子”就找到它,還要“幾下子”,這空間犧牲的是不是不太值得?

          尤其是那些頻繁被訪問的數(shù)據(jù),每次過來都要走 B+ 樹來查詢,這時就會想到,我用一個指針把數(shù)據(jù)的位置記錄下來不就好了?

          這就是「自適應(yīng)哈希索引」(Adaptive Hash Index)。自適應(yīng),顧名思義,MySQL 會自動評估使用自適應(yīng)索引是否值得,如果觀察到建立哈希索引可以提升速度,則建立。

          4、Log Buffer

          The log buffer is the memory area that holds data to be written to the log files on disk.

          從上面架構(gòu)圖可以看到,Log Buffer 里的 redo log,會被刷到磁盤里:

          6181b5924c6b470af0891b99aa0dd277.webp

          Operating System Cache

          在內(nèi)存和磁盤之間,你看到 MySQL 畫了一層叫做 Operating System Cache 的東西,其實(shí)這個不屬于 InnoDB 的能力,而是操作系統(tǒng)為了提升性能,在磁盤前面加的一層高速緩存,這里不展開細(xì)講,感興趣的同學(xué)可以參考下維基百科:Page Cache

          InnoDB 磁盤架構(gòu)

          磁盤里有什么呢?除了表結(jié)構(gòu)定義和索引,還有一些為了高性能和高可靠而設(shè)計(jì)的角色,比如 redo log、undo log、Change Buffer,以及 Doublewrite Buffer 等等.

          有同學(xué)會問,那表的數(shù)據(jù)呢?其實(shí)只要理解了 InnoDB 里的所有表數(shù)據(jù),都以索引(聚簇索引+二級索引)的形式存儲起來,就知道索引已經(jīng)包含了表數(shù)據(jù)。

          1、表空間(Tablespaces)

          從架構(gòu)圖可以看到,Tablespaces 分為五種:

          • The System Tablespace

          • File-Per-Table Tablespaces

          • General Tablespace

          • Undo Tablespaces

          • Temporary Tablespaces

          其中,我們平時創(chuàng)建的表的數(shù)據(jù),可以存放到 The System Tablespace 、File-Per-Table Tablespaces、General Tablespace 三者中的任意一個地方,具體取決于你的配置和創(chuàng)建表時的 sql 語句。

          這里同樣不展開,如何選擇不同的表空間存儲數(shù)據(jù)?不同表空間各自的優(yōu)勢劣勢等等,傳送門:Tablespaces

          2、Doublewrite Buffer

          如果說 Change Buffer 是提升性能,那么 Doublewrite Buffer 就是保證數(shù)據(jù)頁的可靠性。

          怎么理解呢?

          前面提到過,MySQL 以「頁」為讀取和寫入單位,一個「頁」里面有多行數(shù)據(jù),寫入數(shù)據(jù)時,MySQL 會先寫內(nèi)存中的頁,然后再刷新到磁盤中的頁。

          這時問題來了,假設(shè)在某一次從內(nèi)存刷新到磁盤的過程中,一個「頁」刷了一半,突然操作系統(tǒng)或者 MySQL 進(jìn)程奔潰了,這時候,內(nèi)存里的頁數(shù)據(jù)被清除了,而磁盤里的頁數(shù)據(jù),刷了一半,處于一個中間狀態(tài),不尷不尬,可以說是一個「不完整」,甚至是「壞掉的」的頁。

          有同學(xué)說,不是有 Redo Log 么?其實(shí)這個時候 Redo Log 也已經(jīng)無力回天,Redo Log 是要在磁盤中的頁數(shù)據(jù)是正常的、沒有損壞的情況下,才能把磁盤里頁數(shù)據(jù) load 到內(nèi)存,然后應(yīng)用 Redo Log。而如果磁盤中的頁數(shù)據(jù)已經(jīng)損壞,是無法應(yīng)用 Redo Log 的。

          所以,MySQL 在刷數(shù)據(jù)到磁盤之前,要先把數(shù)據(jù)寫到另外一個地方,也就是 Doublewrite Buffer,寫完后,再開始寫磁盤。Doublewrite Buffer 可以理解為是一個備份(recovery),萬一真的發(fā)生 crash,就可以利用 Doublewrite Buffer 來修復(fù)磁盤里的數(shù)據(jù)。

          留個問題,有了 Doublewrite Buffer 后,不就意味著 MySQL 要寫兩次磁盤?性能豈不是很差?

          未完待續(xù)

          讓我們再來回顧一下這張圖:

          27ad3c0742f4901a0e3c7ad3a440c488.webp

          這篇文章,順著這張圖,給大家介紹了 InnoDB 里的每一個成員、成員各自扮演的角色、提供的能力。

          當(dāng)然,這張圖里能表達(dá)的信息是有限的,我習(xí)慣稱這種圖為「架構(gòu)圖」,或者「模塊圖」。

          用 DDD 的話來講,這張圖可以告訴你,MySQL 里有哪些「域」(子域、核心域、通用域、支撐域),配合文字介紹,可以知道這些「域」之間都有什么樣的能力、行為,知道「域」之間一些簡單的交互。

          然而,這張圖并沒有告訴你具體某個業(yè)務(wù)中,這些成員之間要如何配合,來提供一個服務(wù),或者說,如果你的技術(shù)方案里只有這張圖,那你進(jìn)入開發(fā)階段后,最多最多,只能新建幾個微服務(wù)應(yīng)用,新建幾個類和對象,而寫不出這些個微服務(wù)、class 之間如何協(xié)作起來提供一個服務(wù)的代碼。

          所以,下一篇文章,將基于我們這篇文章以及上一篇文章的內(nèi)容,畫出一張足以描述具體業(yè)務(wù)流程的圖。

          什么樣的圖有這種描述力呢?

          自然是 swim-lanes,也就是我們常說的「泳道圖」。

          在那之后,我們將深入到每一個細(xì)分領(lǐng)域,以及具體到一些實(shí)際問題中,來把 MySQL 徹底學(xué)透。

          如果大家想要實(shí)時關(guān)注我更新的文章以及分享的干貨的話,可以關(guān)注我的公眾號Java3y

          • ?獲取海量視頻資源c3c85ebf5b01d39bb4486bc194c393a1.webp

          • 獲取Java精美腦圖c3c85ebf5b01d39bb4486bc194c393a1.webp

          • ?獲取Java學(xué)習(xí)路線c3c85ebf5b01d39bb4486bc194c393a1.webp

          • 獲取開發(fā)常用工具c3c85ebf5b01d39bb4486bc194c393a1.webp

          • ?的PDF電子書c3c85ebf5b01d39bb4486bc194c393a1.webp


          在公眾號下回復(fù)「888」即可獲取!!

          81475d00c3a00f81e3b4f440a21ef0d8.webp

          點(diǎn)個在看7d9af1847ff1b6abe3f85b2e59a1a571.webp,分享到朋友圈d61746f24b1d427666370f4129597133.webp,對我真的很重要!!

          瀏覽 42
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲最大中文字幕在线 | 欧美日韩一区在线观看视频 | 青草午夜视频 | 91大黄片 | 在线看av小说 |