Mysql快速學(xué)習(xí)——《二》: Mysql的InnoDB存儲(chǔ)引擎設(shè)計(jì)

上篇文章介紹了一條SQL語句在數(shù)據(jù)庫中的執(zhí)行流程,當(dāng)SQL執(zhí)行到存儲(chǔ)引擎這里,因?yàn)椴煌囊娴膶?shí)現(xiàn)機(jī)制有所不同桐经,現(xiàn)在就以使用最廣泛的為InnoDB引擎再來細(xì)化說明Innodb在數(shù)據(jù)查詢和更新流程的細(xì)節(jié)。

上篇: Mysql快速學(xué)習(xí)——《一》: Mysql的基礎(chǔ)架構(gòu)
參考: The InnoDB Storage Engine
思維導(dǎo)圖: mysql

存儲(chǔ)引擎層

存儲(chǔ)引擎層(Storage Engines),它決定了 MySQL 會(huì)怎樣存儲(chǔ)數(shù)據(jù)派桩,怎樣讀取和寫入數(shù)據(jù),也在很大程度上決定了 MySQL 的讀寫性能和數(shù)據(jù)可靠性蚌斩。
對(duì)于這么重要的一層能力铆惑,MySQL 提供了極強(qiáng)的擴(kuò)展性,你可以定義自己要使用什么樣的存儲(chǔ)引擎:InnoDB送膳、MyISAM鸭津、MEMORYCSV肠缨,甚至可以自己開發(fā)一個(gè)存儲(chǔ)引擎然后使用它逆趋。

InnoDB存儲(chǔ)引擎

下面是InnoDB 引擎的邏輯架構(gòu)圖, 弄懂它那你對(duì)MYSQL的理解將更進(jìn)一步:


innoDB架構(gòu)圖

從上圖可以看到InnoDB分為2大塊:

  • In-Memory Structures (內(nèi)存部分)
  • On-Disk Structures (磁盤部分)

其中In-Memory Structures (內(nèi)存部分)包括:

  • Buffer Pool - 內(nèi)存緩沖池
  • Change Buffer - 寫交換緩沖池
  • Adaptive Hash Index - 自適應(yīng)哈希索引
  • Log Buffer

On-Disk Structures (磁盤部分), 從架構(gòu)圖可以看到, Tablespaces 分為五種(我們平時(shí)創(chuàng)建的表的數(shù)據(jù),可以存放到 The System Tablespace 晒奕、File-Per-Table Tablespaces闻书、General Tablespace 三者中的任意一個(gè)地方,具體取決于你的配置和創(chuàng)建表時(shí)的 sql 語句)

  • The System Tablespace - 系統(tǒng)表空間
    ---- 在數(shù)據(jù)庫建立的時(shí)候自動(dòng)創(chuàng)建的脑慧,它包含了整個(gè)數(shù)據(jù)庫的數(shù)據(jù)字典魄眉。
  • File-Per-Table Tablespaces - 獨(dú)立表空間
    ---- 是對(duì)The System Tablespace(系統(tǒng)表空間)的一個(gè)更靈活的選擇, 在MySQL 5.6.6和更高版本默認(rèn)啟用的。
  • General Tablespace - 通用表空間
    --- 類似于系統(tǒng)表空間闷袒,常規(guī)表空間是共享表空間坑律,可以存儲(chǔ)多個(gè)表的數(shù)據(jù)。
  • Undo Tablespaces - 回退表空間
  • Temporary Tablespaces

On-Disk Structures (磁盤部分)除了表結(jié)構(gòu)定義和索引囊骤,還有一些為了高性能和高可靠而設(shè)計(jì)的角色晃择,比如

  • redo log
  • undo log
  • Change Buffer
  • Doublewrite Buffer

內(nèi)存部分組件詳解

1. Buffer Pool

緩存表數(shù)據(jù)與索引數(shù)據(jù),把磁盤上的數(shù)據(jù)加載到緩沖池也物,避免每次訪問都進(jìn)行磁盤IO宫屠,起到加速訪問的作用。


InnoDB緩沖池策略
  • 按頁(4K)讀取
    磁盤讀寫滑蚯,并不是按需讀取浪蹂,而是按頁讀取抵栈,一次至少讀一頁數(shù)據(jù)(一般是4K),如果未來要讀取的數(shù)據(jù)就在頁中坤次,就能夠省去后續(xù)的磁盤IO古劲,提高效率。
  • “集中讀寫”的原則(預(yù)讀)
    數(shù)據(jù)訪問缰猴,通常都遵循“集中讀寫”的原則绢慢,使用一些數(shù)據(jù),大概率會(huì)使用附近的數(shù)據(jù)洛波,這就是所謂的“局部性原理”胰舆。InnoDB會(huì)把一些“可能要訪問”的頁提前加入緩沖池,避免未來的磁盤IO操作蹬挤。

傳統(tǒng)LRU緩沖池算法

為了減少數(shù)據(jù)移動(dòng)缚窿,LRU一般用鏈表實(shí)現(xiàn)。最常見的玩法是焰扳,把入緩沖池的頁放到LRU的頭部倦零,作為最近訪問的元素,從而最晚被淘汰吨悍。這里又分兩種情況:
(1)頁已經(jīng)在緩沖池里扫茅,那就只做“移至”LRU頭部的動(dòng)作,而沒有頁被淘汰育瓜;
(2)頁不在緩沖池里葫隙,除了做“放入”LRU頭部的動(dòng)作,還要做“淘汰”LRU尾部頁的動(dòng)作躏仇;

InnoDB并不直接使用傳統(tǒng)的LRU緩沖池算法, 因?yàn)閭鹘y(tǒng)的LRU緩沖池算法會(huì)出現(xiàn)以下問題:
(1)預(yù)讀失效: 由于預(yù)讀(Read-Ahead)恋脚,提前把頁放入了緩沖池,但最終MySQL并沒有從頁中讀取數(shù)據(jù)焰手,稱為預(yù)讀失效糟描。
(2)緩沖池污染: 當(dāng)某一個(gè)SQL語句,要批量掃描大量數(shù)據(jù)時(shí)书妻,可能導(dǎo)致把緩沖池的所有頁都替換出去船响,導(dǎo)致大量熱數(shù)據(jù)被換出,MySQL性能急劇下降躲履,這種情況叫緩沖池污染见间。


InnoDB對(duì)傳統(tǒng)LRU旳優(yōu)化
  • 預(yù)讀失敗優(yōu)化 - 新老生代機(jī)制
    (1)將LRU分為兩個(gè)部分:新生代(new sublist) + 老生代(old sublist)
    (2)新老生代收尾相連,即:新生代的尾(tail)連接著老生代的頭(head)崇呵;
    (3)新頁(例如被預(yù)讀的頁)加入緩沖池時(shí)缤剧,只加入到老生代頭部
    (4)如果數(shù)據(jù)真正被讀认谠(預(yù)讀成功)域慷,才會(huì)加入到新生代的頭部
    (5)如果數(shù)據(jù)沒有被讀取,則會(huì)比新生代里的“熱數(shù)據(jù)頁”更早被淘汰出緩沖池


    新老生代機(jī)制
  • 緩沖池污染優(yōu)化 - 老生代停留時(shí)間窗口機(jī)制
    (1)假設(shè)T=老生代停留時(shí)間窗口;
    (2)插入老生代頭部的頁犹褒,即使立刻被訪問抵窒,并不會(huì)立刻放入新生代頭部;
    (3)只有滿足“被訪問”并且“在老生代停留時(shí)間”大于T叠骑,才會(huì)被放入新生代頭部李皇;


    老生代停留時(shí)間窗口機(jī)制

buffer_pool相關(guān)重要參數(shù)
  • innodb_buffer_pool_size
    配置緩沖池的大小,在內(nèi)存允許的情況下宙枷,DBA往往會(huì)建議調(diào)大這個(gè)參數(shù)掉房,越多數(shù)據(jù)和索引放到內(nèi)存里,數(shù)據(jù)庫的性能會(huì)越好慰丛。
  • innodb_old_blocks_pct
    老生代占整個(gè)LRU鏈長(zhǎng)度的比例卓囚,默認(rèn)是37,即整個(gè)LRU中新生代與老生代長(zhǎng)度比例是63:37诅病。
  • innodb_old_blocks_time
    老生代停留時(shí)間窗口哪亿,單位是毫秒,默認(rèn)是1000贤笆,即同時(shí)滿足“被訪問”與“在老生代停留時(shí)間超過1秒”兩個(gè)條件蝇棉,才會(huì)被插入到新生代頭部。

2. Change Buffer

寫請(qǐng)求的處理流程

(1)如果索引頁不在buffer pool中, 則先把索引頁芥永,從磁盤加載到緩沖池篡殷,一次磁盤隨機(jī)讀操作;
(2)修改緩沖池中的頁埋涧,一次內(nèi)存操作贴唇;
(3)寫入redo log,一次磁盤順序?qū)懖僮鳎?/p>

寫請(qǐng)求處理

是否會(huì)出現(xiàn)一致性問題呢飞袋?

不會(huì), 因?yàn)?
(1)讀取戳气,會(huì)命中緩沖池的頁;
(2)緩沖池LRU數(shù)據(jù)淘汰巧鸭,會(huì)將“臟頁”刷回磁盤瓶您;
(3)數(shù)據(jù)庫異常奔潰,能夠從redo log中恢復(fù)數(shù)據(jù)纲仍;


利用Change Buffer進(jìn)行優(yōu)化

上述場(chǎng)景中, 被讀取的數(shù)據(jù)沒有命中緩沖池的時(shí)候呀袱,會(huì)先從磁盤索引頁到緩沖池中, 這樣至少產(chǎn)生一次磁盤IO,對(duì)于寫多讀少的業(yè)務(wù)場(chǎng)景郑叠,性能壓力會(huì)劇增, 于是InnoDB引入了Change Buffer:

  • 當(dāng)對(duì)頁進(jìn)行了寫操作夜赵,并不會(huì)立刻將磁盤頁加載到緩沖池
  • 先把頁的寫操作記錄到緩沖變更池(buffer changes)
  • 等未來數(shù)據(jù)被讀取時(shí),再將數(shù)據(jù)合并(merge)恢復(fù)到緩沖池中

寫緩沖的目的是降低寫操作的磁盤IO乡革,提升數(shù)據(jù)庫性能寇僧。


Change Buffer

在內(nèi)存中摊腋,Change Buffer占用Buffer Pool的一部分。在磁盤上嘁傀,Change Buffer是系統(tǒng)表空間的一部分兴蒸,其中的索引會(huì)在關(guān)閉數(shù)據(jù)庫服務(wù)器時(shí)更改。


Change Buffer相關(guān)參數(shù)配置
  • 配置Change Pool最大大小
    ---innodb_change_buffer_max_size: 允許將Change Buffer的最大大小配置為緩沖池總大小的百分比细办。默認(rèn)情況下, innodb_change_buffer_max_size設(shè)置為25.最大設(shè)置為50橙凳。
  • 配置Change Buffer的適用范圍
    ---innodb_change_buffering: all | none | inserts | deletes | changes | purges

3. Adaptive Hash Index

AHI是InnoDB索引的索引, 為了在索引很大時(shí)快速得到數(shù)據(jù)。
AHI 在實(shí)現(xiàn)上就是一個(gè)哈希表:從某個(gè)檢索條件到某個(gè)數(shù)據(jù)頁的哈希表笑撞,仿佛并不復(fù)雜岛啸,但其中的關(guān)竅在于哈希表不能太大(哈希表維護(hù)本身就有成本,哈希表太大則成本會(huì)高于收益)茴肥,又不能太小(太小則緩存命中率太低值戳,沒有任何收益)。
AHI建立需要遵循以下約束:
(1)某個(gè)索引樹要被使用足夠多次
(2)該索引樹上的某個(gè)檢索條件要被經(jīng)常使用
(3)該索引樹上的某個(gè)數(shù)據(jù)頁要被經(jīng)常使用

4. Log Buffer

當(dāng)在MySQL中對(duì)InnoDB表進(jìn)行更改時(shí)炉爆,這些更改首先存儲(chǔ)在InnoDB日志緩沖區(qū)的內(nèi)存中堕虹,然后寫入通常稱為重做日志(redo logs)的InnoDB日志文件中。
日志緩沖區(qū)是內(nèi)存存儲(chǔ)區(qū)域芬首,用于保存要寫入磁盤上的日志文件的數(shù)據(jù)赴捞。日志緩沖區(qū)大小由innodb_log_buffer_size 變量定義,默認(rèn)大小為16MB郁稍。
日志緩沖區(qū)的內(nèi)容定期刷新到磁盤赦政。較大的日志緩沖區(qū)可以運(yùn)行大型事務(wù),而無需在事務(wù)提交之前將重做日志數(shù)據(jù)寫入磁盤耀怜。因此恢着,如果有更新,插入或刪除許多行的事務(wù)财破,則增加日志緩沖區(qū)的大小可以節(jié)省磁盤I/O掰派。

磁盤部分組件詳解

1. redo log

在更新操作時(shí),會(huì)先更新Buffer Pool中的數(shù)據(jù)然后再去操作磁盤左痢,但是在極端情況下會(huì)出現(xiàn)系統(tǒng)宕機(jī)或者斷電導(dǎo)致磁盤還未更新就丟失了數(shù)據(jù)靡羡,此時(shí)需要把對(duì)內(nèi)存所做的修改寫入到一個(gè)redo log buffer里去,這里也是一個(gè)內(nèi)存緩沖區(qū)俊性,用于存放redo日志的略步。

2. undo log

如果執(zhí)行一個(gè)更新語句,且這個(gè)語句還在事務(wù)里的話定页,在事務(wù)提交以前趟薄,我們都可以選擇回滾,而這部分回滾的數(shù)據(jù)典徊,就是未更新以前的數(shù)據(jù)杭煎,它是保存在undo日志里的恩够。

3. Change Buffer

--

4. Doublewrite Buffer

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


怎么理解呢玫鸟?

前面提到過导绷,MySQL 以「頁」為讀取和寫入單位犀勒,一個(gè)「頁」里面有多行數(shù)據(jù),寫入數(shù)據(jù)時(shí)妥曲,MySQL 會(huì)先寫內(nèi)存中的頁贾费,然后再刷新到磁盤中的頁。
這時(shí)問題來了檐盟,假設(shè)在某一次從內(nèi)存刷新到磁盤的過程中褂萧,一個(gè)「頁」刷了一半,突然操作系統(tǒng)或者 MySQL 進(jìn)程奔潰了葵萎,這時(shí)候导犹,內(nèi)存里的頁數(shù)據(jù)被清除了,而磁盤里的頁數(shù)據(jù)羡忘,刷了一半谎痢,處于一個(gè)中間狀態(tài),不尷不尬卷雕,可以說是一個(gè)「不完整」节猿,甚至是「壞掉的」的頁。

有同學(xué)說漫雕,不是有 Redo Log 么滨嘱?其實(shí)這個(gè)時(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ù)寫到另外一個(gè)地方,也就是 Doublewrite Buffer铣减,寫完后她君,再開始寫磁盤。Doublewrite Buffer 可以理解為是一個(gè)備份(recovery)葫哗,萬一真的發(fā)生 crash缔刹,就可以利用 Doublewrite Buffer 來修復(fù)磁盤里的數(shù)據(jù)球涛。

代碼塊
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市校镐,隨后出現(xiàn)的幾起案子亿扁,更是在濱河造成了極大的恐慌,老刑警劉巖鸟廓,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件从祝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡引谜,警方通過查閱死者的電腦和手機(jī)牍陌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來员咽,“玉大人毒涧,你說我怎么就攤上這事”词遥” “怎么了契讲?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)滑频。 經(jīng)常有香客問我捡偏,道長(zhǎng),這世上最難降的妖魔是什么误趴? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任霹琼,我火速辦了婚禮,結(jié)果婚禮上凉当,老公的妹妹穿的比我還像新娘枣申。我一直安慰自己,他們只是感情好看杭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布忠藤。 她就那樣靜靜地躺著,像睡著了一般楼雹。 火紅的嫁衣襯著肌膚如雪模孩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天贮缅,我揣著相機(jī)與錄音榨咐,去河邊找鬼。 笑死谴供,一個(gè)胖子當(dāng)著我的面吹牛块茁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼数焊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼永淌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起佩耳,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤遂蛀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后干厚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李滴,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年萍诱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悬嗓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片污呼。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡裕坊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出燕酷,到底是詐尸還是另有隱情籍凝,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布苗缩,位于F島的核電站饵蒂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酱讶。R本人自食惡果不足惜退盯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泻肯。 院中可真熱鬧渊迁,春花似錦、人聲如沸灶挟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稚铣。三九已至箱叁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惕医,已是汗流浹背耕漱。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抬伺,地道東北人螟够。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沛简,于是被迫代替她去往敵國(guó)和親齐鲤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斥废,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容