主頁(yè) > 知識(shí)庫(kù) > MySQL InnoDB存儲(chǔ)引擎的深入探秘

MySQL InnoDB存儲(chǔ)引擎的深入探秘

熱門(mén)標(biāo)簽:百度地圖標(biāo)注后不顯示 電銷(xiāo)機(jī)器人 行業(yè) 俄國(guó)地圖標(biāo)注app 淄博400電話申請(qǐng) 溫州瑞安400電話怎么申請(qǐng) 南昌高頻外呼系統(tǒng)哪家公司做的好 電話機(jī)器人市場(chǎng)趨勢(shì) 昆明電信400電話辦理 電銷(xiāo)機(jī)器人各個(gè)細(xì)節(jié)介紹

前言

在MySQL中InnoDB屬于存儲(chǔ)引擎層,并以插件的形式集成在數(shù)據(jù)庫(kù)中。從MySQL5.5.8開(kāi)始,InnoDB成為其默認(rèn)的存儲(chǔ)引擎。InnoDB存儲(chǔ)引擎支持事務(wù)、其設(shè)計(jì)目標(biāo)主要是面向OLTP的應(yīng)用,主要特點(diǎn)有:支持事務(wù)、行鎖設(shè)計(jì)支持高并發(fā)、外鍵支持、自動(dòng)崩潰恢復(fù)、聚簇索引的方式組織表結(jié)構(gòu)等。

體系架構(gòu)

InnoDB存儲(chǔ)引擎是由內(nèi)存池、后臺(tái)線程、磁盤(pán)存儲(chǔ)三大部分組成。

線程

InnoDB 使用的是多線程模型, 其后臺(tái)有多個(gè)不同的線程負(fù)責(zé)處理不同的任務(wù)

Master Thread

Master Thread是最核心的一個(gè)后臺(tái)線程,主要負(fù)責(zé)將緩沖池中的數(shù)據(jù)異步刷新到磁盤(pán),保證數(shù)據(jù)的一致性。包括臟頁(yè)刷新、合并插入緩沖、UNDO頁(yè)的回收等。

IO Thread

在 InnoDB 存儲(chǔ)引擎中大量使用了異步IO(Async IO)來(lái)處理寫(xiě)IO請(qǐng)求, IO Thread的工作主要是負(fù)責(zé)這些 IO 請(qǐng)求的回調(diào)。

Purge Thread

事務(wù)提交后,其所使用的undo log可能不再需要,因此需要Purge Thread來(lái)回收已經(jīng)分配并使用的UNDO頁(yè)。InnoDB支持多個(gè)Purge Thread, 這樣做可以加快UNDO頁(yè)的回收,提高CPU的使用率以及提升存儲(chǔ)引擎的性能。

Page Cleaner Thread

Page Cleaner Thread的作用是取代Master Thread中臟頁(yè)刷新的操作,其目的是減輕原Master Thread的工作及對(duì)于用戶查詢線程的阻塞,進(jìn)一步提高InnoDB存儲(chǔ)引擎的性能。

內(nèi)存

InnoDB 存儲(chǔ)引擎內(nèi)存的結(jié)構(gòu)

緩沖池

InnoDB存儲(chǔ)引擎是基于磁盤(pán)存儲(chǔ)的,并將其中的記錄按照頁(yè)的方式進(jìn)行管理。但是由于CPU速度和磁盤(pán)速度之間的鴻溝,基于磁盤(pán)的數(shù)據(jù)庫(kù)系統(tǒng)通常使用緩沖池記錄來(lái)提高數(shù)據(jù)庫(kù)的的整體性能。

緩沖池其實(shí)就是通過(guò)內(nèi)存的速度來(lái)彌補(bǔ)磁盤(pán)速度較慢對(duì)數(shù)據(jù)庫(kù)性能的影響。在數(shù)據(jù)庫(kù)進(jìn)行讀取操作時(shí),首先將磁盤(pán)中的頁(yè)放入緩沖池中,下次再讀取相同頁(yè)時(shí),首先從緩沖池中獲取該頁(yè)數(shù)據(jù),起到高速緩存的作用。

數(shù)據(jù)的修改操作,則首先修改在緩沖池中的頁(yè)數(shù)據(jù),然后使用一種稱(chēng)為Checkpoint的機(jī)制刷新到磁盤(pán)上。

緩沖池的大小直接影響數(shù)據(jù)庫(kù)的整體性能,對(duì)于InnoDB存儲(chǔ)引擎而言,緩沖池配置通過(guò)參數(shù) innodb_buffer_pool_size 來(lái)設(shè)置。使用 SHOW VARIABLES LIKE 'innodb_buffer_pool_size' 命令可查看緩沖池配置:

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size' \G
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_size
 Value: 134217728
1 row in set (0.01 sec)

緩沖池中緩存的數(shù)據(jù)頁(yè)類(lèi)型有: 索引頁(yè)、undo頁(yè)、插入緩沖、自適應(yīng)哈希索引、InnoDB鎖信息、數(shù)據(jù)字典信息等,索引頁(yè)和數(shù)據(jù)頁(yè)占緩沖池很大的一部分。

重做日志緩沖

緩沖池中的頁(yè)數(shù)據(jù)比磁盤(pán)要新時(shí),需要將新數(shù)據(jù)刷新到磁盤(pán)中。InnoDB采用Write Ahead Log策略來(lái)刷新數(shù)據(jù),即當(dāng)事務(wù)提交時(shí),先寫(xiě)入重做日志緩沖,重做日志緩沖會(huì)按一定頻率刷新到重置日志文件中,然后臟頁(yè)會(huì)根據(jù)checkpoint機(jī)制刷新到磁盤(pán)。

重做日志緩沖不需要設(shè)置很大,通常情況下8M能滿足大部分的應(yīng)用場(chǎng)景。重做日志支持一下三種情況觸發(fā)刷新:

  • Master Thread每一秒將重做日志緩沖刷新到重做日志文件
  • 每次事務(wù)提交時(shí)將重做日志緩沖刷新到重做日志文件
  • 當(dāng)重做日志緩沖池剩余空間小于1/2時(shí),重做日志緩沖刷新到重做日志文件


額外內(nèi)存池

在InnoDB存儲(chǔ)引擎中,對(duì)內(nèi)存的管理是通過(guò)一種稱(chēng)為內(nèi)存堆的方式進(jìn)行的。在對(duì)一些數(shù)據(jù)結(jié)構(gòu)本身的內(nèi)存進(jìn)行分配時(shí),需要從額外的內(nèi)存池中進(jìn)行申請(qǐng),當(dāng)該區(qū)域的內(nèi)存不夠時(shí),會(huì)從緩沖池中進(jìn)行申請(qǐng)。


InnoDB支持的鎖有:

  • 共享鎖和排它鎖
  • 意向鎖
  • 記錄鎖
  • 間隙鎖
  • 自增鎖

共享鎖和排他鎖

InnoDB引擎實(shí)現(xiàn)了兩種標(biāo)準(zhǔn)的行級(jí)鎖,共享鎖(shared (S) locks) 和排他鎖 (exclusive (X) locks)。共享鎖允許一個(gè)占有鎖的事務(wù)去讀取一行數(shù)據(jù),排它鎖則允許事務(wù)對(duì)某一行記錄進(jìn)行寫(xiě)操作。

如果一個(gè)事務(wù)持有了一個(gè)共享鎖,其他事務(wù)仍然可以獲取這行記錄的共享鎖,但不能獲取到這行記錄的排它鎖。當(dāng)一個(gè)事務(wù)獲取到了某一行的排它鎖,則其他事務(wù)將無(wú)法再獲取這行記錄的共享鎖和排它鎖。

意向鎖

在InnoDB中,意向鎖是一種表級(jí)鎖,分為共享鎖和排他鎖:

  • 意向共享鎖:將要去獲取某一行的共享鎖
  • 意向排它鎖:將要去獲取某一行的排它鎖

事務(wù)在獲取共享/排它鎖之前必須先獲取意向共享/排它鎖,意向鎖不會(huì)阻塞其他任何對(duì)表的操作,他只是告訴其他事務(wù)他將要去獲取某一行的共享鎖或者排他鎖。

記錄鎖

記錄是是作用在索引上的一種鎖,他鎖住的是某一條記錄的索引而非記錄本身,如果當(dāng)前表沒(méi)有索引那么InnoDB將會(huì)為其創(chuàng)建一個(gè)隱藏的聚集索引,而Record Locks將會(huì)鎖住這個(gè)隱藏的聚集索引。

間隙鎖

間隙鎖和記錄鎖一樣也是作用在索引上,不同的是記錄鎖只作用于一條索引記錄而間隙鎖可以鎖住一個(gè)范圍內(nèi)的索引。間隙鎖在InnoDB的唯一作用就是防止其他事務(wù)的插入操作,以此防止幻讀的發(fā)生。

自增鎖

自增鎖是一種特殊的表級(jí)鎖,他只作用在包含自增列的插入操作時(shí)。當(dāng)一個(gè)事務(wù)正在插入一條數(shù)據(jù)時(shí),其他的任何事務(wù)都必須等待整個(gè)事務(wù)完成插入操作,在取獲取鎖來(lái)執(zhí)行插入操作。

事務(wù)

ACID

事務(wù)是數(shù)據(jù)庫(kù)作為OLTP最為重要的特性,說(shuō)起事務(wù)不得不提起ACID四個(gè)基本特性:

  • 原子性(Atomicity) :事務(wù)最小工作單元,要么全成功,要么全失敗
  • 一致性(Consistency): 事務(wù)開(kāi)始和結(jié)束后,數(shù)據(jù)庫(kù)的完整性不會(huì)被破壞
  • 隔離性(Isolation) :不同事務(wù)之間互不影響,四種隔離級(jí)別為RU(讀未提交)、RC(讀已提交)、RR(可重復(fù)讀)、SERIALIZABLE (串行化)
  • 持久性(Durability) :事務(wù)提交后,對(duì)數(shù)據(jù)的修改是永久性的,即使系統(tǒng)故障也不會(huì)丟失

InnoDB的原子性、持久性和一致性主要是通過(guò)Redo Log、Undo Log和Force Log at Commit機(jī)制機(jī)制來(lái)完成的。Redo Log用于在崩潰時(shí)恢復(fù)數(shù)據(jù),Undo Log用于對(duì)事務(wù)的影響進(jìn)行撤銷(xiāo),也可以用于多版本控制。而Force Log at Commit機(jī)制保證事務(wù)提交后Redo Log日志都已經(jīng)持久化。隔離性則是由鎖和MVCC來(lái)保證的。

隔離級(jí)別

在MySQL中,事務(wù)有4種隔離級(jí)別,分別是:

  • Read Uncommitted 未提交讀
  • Read Committed 已提交讀
  • Repeatable Read 可重復(fù)讀
  • Serializable 可串行化

在理解四種隔離級(jí)別之前,我們需要先了解另外三個(gè)名詞:

  • 臟讀

    a事務(wù)會(huì)讀取到b事務(wù)還未提交的數(shù)據(jù),但是b事務(wù)由于某種原因進(jìn)行回滾操作,這樣,a事務(wù)讀取的數(shù)據(jù)是不可用的,進(jìn)而會(huì)造成一些異常結(jié)果。

  • 不可重復(fù)讀

    a事務(wù)周期內(nèi)對(duì)某一數(shù)據(jù)多次查詢,同時(shí)這些數(shù)據(jù)在b事務(wù)中進(jìn)行了update或delete操作。那么a事務(wù)每次查詢出來(lái)的結(jié)果可能都不一樣。

  • 幻讀

    幻讀的結(jié)果其實(shí)和不可重復(fù)讀是一樣的表現(xiàn),差異就在于不可重復(fù)讀主要是針對(duì)其他事務(wù)進(jìn)行了編輯(update)和刪除(delete)操作。而幻讀主要是針對(duì)插入(insert)操作。也就是在一個(gè)事務(wù)生命周期內(nèi),會(huì)查詢到另外一個(gè)事務(wù)新插入的數(shù)據(jù)。

Read uncommitted 未提交讀

未提交讀,這種情況下,一個(gè)事務(wù)a可以看到另一個(gè)事務(wù)b未提交的數(shù)據(jù),如果此時(shí)事務(wù)b發(fā)生回滾,那么事務(wù)a拿到的就是臟數(shù)據(jù),這也就是臟讀的含義。

此隔離級(jí)別在MySQL InnoDB一般不推薦使用。

Read Committed 已提交讀

已提交讀,一個(gè)事務(wù)從開(kāi)始直到提交之前,所做的任何修改對(duì)其他事務(wù)都是不可見(jiàn)的。解決了臟讀問(wèn)題,但是存在幻讀現(xiàn)象。

Repeatable Read 可重復(fù)讀

可重復(fù)讀,該級(jí)別保證在同一事務(wù)中多次讀取同樣記錄的結(jié)果是一致的,在InnoDB存儲(chǔ)引擎中同時(shí)解決了幻讀和不可重復(fù)讀問(wèn)題。

InnoDB引擎通過(guò)使用Next-Key Lock解決了幻讀的問(wèn)題。Next-Key Lock是行鎖和間隙鎖的組合,當(dāng)InnoDB掃描索引記錄的時(shí)候,會(huì)首先對(duì)索引記錄加上行鎖(Record Lock),再對(duì)索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。加上間隙鎖之后,其他事務(wù)就不能在這個(gè)間隙修改或者插入記錄。

Serializable 可串行化

Serializable 是最高的隔離級(jí)別,它通過(guò)強(qiáng)制事務(wù)串行執(zhí)行,避免了幻讀的問(wèn)題,但是 Serializable 會(huì)在讀取的每一行數(shù)據(jù)上都加鎖,所以可能導(dǎo)致大量的超時(shí)和鎖爭(zhēng)用的問(wèn)題,因此并發(fā)度急劇下降,在MySQL InnoDB同樣不被建議使用。

開(kāi)啟事務(wù)

  • BEGIN、BEGIN WORK、START TRANSACTION

    執(zhí)行BEGIN命令不會(huì)真正在引擎層開(kāi)啟新事務(wù),僅僅是為當(dāng)前線程設(shè)定標(biāo)記,表示為顯式開(kāi)啟的事務(wù)。

  • START TRANSACTION READ ONLY

    開(kāi)啟只讀事務(wù),當(dāng)MySQL Server接收到任何數(shù)據(jù)更改的SQL時(shí),都會(huì)直接拒絕修改并返回錯(cuò)誤,此錯(cuò)我不會(huì)進(jìn)入引擎層。

  • START TRANSACTION READ WRITE

    允許super用戶在當(dāng)前線程只讀狀態(tài)為true的情況下啟動(dòng)讀寫(xiě)事務(wù)。

  • START TRANSACTION WITH CONSISTENT SNAPSHOT

    開(kāi)啟事務(wù)會(huì)進(jìn)入引擎層,并開(kāi)啟一個(gè)readview。只有在RR隔離級(jí)別下,這種操作才有效,否則會(huì)報(bào)錯(cuò)。

Undo log

在數(shù)據(jù)進(jìn)行修改時(shí)會(huì)記錄相應(yīng)的undo日志,如果事務(wù)失敗或者回滾,可以借助記錄的undo日志進(jìn)行回滾。Undo log是邏輯日志,記錄更改前的數(shù)據(jù)鏡像。在修改時(shí)如果同時(shí)需要讀取當(dāng)前數(shù)據(jù)的時(shí)候,它可以根據(jù)版本信息分析出該行記錄以前版本的數(shù)據(jù)。另外Undo log也會(huì)產(chǎn)生重做日志,因?yàn)閁ndo log也要進(jìn)行持久化保護(hù)。

事務(wù)提交

  1. 使用全局事務(wù)ID產(chǎn)生器生成事務(wù)NO,將當(dāng)前連接的事務(wù)指針(trx_t)添加到全局提交事務(wù)鏈表(trx_serial_list)中
  2. 標(biāo)記undo,如果這個(gè)事務(wù)只使用了一個(gè)UndoPage且使用量小于3/4個(gè)Page,則把這個(gè)Page標(biāo)記為T(mén)RX_UNDO_CACHED,如果不滿足且是insert undo則標(biāo)記為T(mén)RX_UNDO_TO_FREE,否則undo為update undo則標(biāo)記為T(mén)RX_UNDO_TO_PURGE。標(biāo)記為T(mén)RX_UNDO_CACHED的undo會(huì)被引擎回收。
  3. 把update undo放入所在undo segment的history list,并遞增rseg_history_len(全局)。同時(shí)更新Page上的TRX_UNDO_TRX_NO, 如果刪除了數(shù)據(jù),則重置delete_mark
  4. 把undate undo從update_undo_list中刪除,如果被標(biāo)記為T(mén)RX_UNDO_CACHED,則加入到update_undo_cached隊(duì)列中
  5. mtr_commit(日志undo/redo寫(xiě)入公共緩沖區(qū)),至此,在文件層次事務(wù)提交。這個(gè)時(shí)候即使crash,重啟后依然能保證事務(wù)是被提交的。接下來(lái)要做的是內(nèi)存數(shù)據(jù)狀態(tài)的更新(trx_commit_in_memory)
  6. 只讀事務(wù)只需要把readview從全局readview鏈表中移除,然后重置trx_t結(jié)構(gòu)體里面的信息即可。讀寫(xiě)事務(wù)首先需要是設(shè)置事務(wù)狀態(tài)為T(mén)RX_STATE_COMMITTED_IN_MEMORY,釋放所有行鎖并且將trx_t從rw_trx_list中移除,readview從全局readview鏈表中移除。如果有insert undo則在這里移除,如果有update undo則喚醒Purge線程進(jìn)行垃圾清理,最后重置trx_t里的信息,便于下一個(gè)事務(wù)使用

回滾

  • 如果是只讀事務(wù),則直接返回
  • 判斷當(dāng)前是回滾整個(gè)事務(wù)還是部分事務(wù),如果是部分事務(wù),則記錄下需要保留多少個(gè)Undo log,多余的全進(jìn)行回滾
  • 從update undo和insert undo中找出最后一條undo,從這條undo開(kāi)始回滾
  • 如果是update undo則將標(biāo)記為刪除的記錄清理標(biāo)記,更新過(guò)的數(shù)據(jù)回滾到最老的版本。如果是insert undo則直接刪除聚集索引和二級(jí)索引
  • 如果所有undo都已經(jīng)被回滾或者回滾到了指定的undo則停止,把Undo log刪除

索引

InnoDB引擎使用B+樹(shù)作為索引結(jié)構(gòu),主鍵索引的葉子節(jié)點(diǎn)data域保存了完整的字段數(shù)據(jù),非主鍵索引的葉子節(jié)點(diǎn)保存了指向主鍵的值數(shù)據(jù)。

上圖是 InnoDB 主索引(同時(shí)也是數(shù)據(jù)文件)的示意圖,可以看到葉節(jié)點(diǎn)包含了完整的數(shù)據(jù)記錄,這種索引叫做聚集索引。因?yàn)?InnoDB 的數(shù)據(jù)文件本身要按主鍵聚集,所以 InnoDB 要求表必須有主鍵,如果沒(méi)有顯式指定,則 MySQL 系統(tǒng)會(huì)自動(dòng)選擇一個(gè)可以唯一標(biāo)識(shí)數(shù)據(jù)記錄的列作為主鍵,如果不存在這種列,則 MySQL 自動(dòng)為 InnoDB 表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長(zhǎng)度為6個(gè)字節(jié),類(lèi)型為長(zhǎng)整形。

InnoDB 的輔助索引 data 域存儲(chǔ)相應(yīng)記錄主鍵的值而不是地址。換句話說(shuō),InnoDB 的所有輔助索引都引用主鍵作為 data 域。聚集索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。

結(jié)尾

對(duì)于MySQL InnoDB的諸多特性,本文只介紹了很小的一部分,感興趣的同學(xué)可閱讀 《MySQL技術(shù)內(nèi)幕:InnoDB存儲(chǔ)引擎》了解更多相關(guān)知識(shí)。

好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

您可能感興趣的文章:
  • MySql優(yōu)化之InnoDB,4GB內(nèi)存,多查詢的my.ini中文配置方案詳解
  • 簡(jiǎn)述MySQL InnoDB存儲(chǔ)引擎
  • MySQL 學(xué)習(xí)總結(jié) 之 初步了解 InnoDB 存儲(chǔ)引擎的架構(gòu)設(shè)計(jì)
  • MySQL學(xué)習(xí)(七):Innodb存儲(chǔ)引擎索引的實(shí)現(xiàn)原理詳解
  • MySQL存儲(chǔ)引擎MyISAM與InnoDB區(qū)別總結(jié)整理
  • 詳解分析MySQL8.0的內(nèi)存消耗
  • MySQL 內(nèi)存表和臨時(shí)表的用法詳解
  • MySQL8.0內(nèi)存相關(guān)參數(shù)總結(jié)
  • MySql減少內(nèi)存占用的方法詳解
  • 詳解MySQL InnoDB存儲(chǔ)引擎的內(nèi)存管理

標(biāo)簽:安徽 甘南 洛陽(yáng) 吐魯番 嘉峪關(guān) ???/a> 拉薩 葫蘆島

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL InnoDB存儲(chǔ)引擎的深入探秘》,本文關(guān)鍵詞  MySQL,InnoDB,存儲(chǔ),引擎,的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《MySQL InnoDB存儲(chǔ)引擎的深入探秘》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于MySQL InnoDB存儲(chǔ)引擎的深入探秘的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章