一文搞懂 MySQL InnoDB架構(gòu) Buffer Pool、Change Buffer、自適應(yīng)哈希索引、Log Buffer
InnoDB 架構(gòu)誕生
2003 年 12 月 24 日,平安夜,林淵從維修臺(tái)猛然驚醒,耳邊是 DBA 的怒吼:"商品庫(kù)又被表鎖卡死了!每秒 500 單變 5 單!"
"小林,數(shù)據(jù)庫(kù)鎖表了,MyISAM 的表鎖就是定時(shí)炸彈!我們要突破技術(shù)封鎖,開發(fā)一套劃時(shí)代的存儲(chǔ)引擎“ CTO 對(duì)小林說(shuō)道。
生在 2025 年作為互聯(lián)網(wǎng)打工牛馬的林淵,學(xué)過(guò)很多關(guān)于 MySQL 的技術(shù),記憶如潮水涌入——2025 年的 InnoDB 架構(gòu)圖在他腦中展開,InnoDB 內(nèi)存架構(gòu)、磁盤架構(gòu);
以及那些 Buffer Pool、Change Buffer 的代碼如同梵高星月夜般絢爛,解鎖《InnoDB 設(shè)計(jì)圖鑒》,準(zhǔn)備揚(yáng)名立萬(wàn)。
于是他在京都國(guó)際數(shù)據(jù)庫(kù)提交一篇提案:《論行級(jí)鎖與內(nèi)存緩沖池——下一代存儲(chǔ)引擎設(shè)計(jì)提案》,附件性能對(duì)比圖震撼業(yè)界。
場(chǎng)景 | MyISAM | "InnoDB"原型 |
100 萬(wàn)并發(fā)更新 | 崩潰 | TPS 18,492 |
范圍查詢 | 12.8s | 0.3s |
InnoDB 內(nèi)存結(jié)構(gòu)主要包含 Buffer Pool 、Change Buffer 、Adaptive Hash Index (自適應(yīng)哈希索引)和 Log Buffer。
Buffer Pool
Buffer Pool 是主內(nèi)存中的一個(gè)區(qū)域,它在訪問(wèn)時(shí)緩存表和索引數(shù)據(jù)。Buffer Pool 允許頻繁使用的數(shù)據(jù)直接從內(nèi)存中訪問(wèn),從而加快處理速度。
Buffer Pool 是 InnoDB 引擎的核心內(nèi)存組件,采用預(yù)分配的連續(xù)內(nèi)存空間,默認(rèn)大小通過(guò) innodb_buffer_pool_size
配置(建議設(shè)置為物理內(nèi)存的 60-80%)。
其本質(zhì)是一個(gè)基于頁(yè)(Page)的緩存系統(tǒng),通過(guò) Page Directory 和 Free List 實(shí)現(xiàn)高效內(nèi)存管理。
為了提高大量讀取操作的效率,緩沖池被劃分為可以潛在地包含多行的頁(yè)面。
Buffer Pool LRU 算法
Buffer Pool 使用 LRU 算法的變體進(jìn)行管理。當(dāng)需要空間向緩沖池中添加新頁(yè)面時(shí),最近最少使用的頁(yè)面被移除,并將新頁(yè)面添加到列表的中間。
這種中間插入策略將列表視為兩個(gè)子列表:
- 在頭部,一個(gè)包含最近訪問(wèn)的新(“young”)頁(yè)面的子列表,稱之為 「New Sublist」
- 在尾部,一個(gè)包含較舊(“Old”)頁(yè)面的子列表,稱之為 「Old Sublist」,這些頁(yè)面數(shù)據(jù)通常是較少被訪問(wèn)的。
圖片
算法將頻繁使用的頁(yè)面保留在「New Sublist」中?!窸ld Sublist」包含使用頻率較低的頁(yè)面。
默認(rèn)情況下,算法按以下方式運(yùn)行:
- Buffer Pool 的 3/8 用于「Old Sublist」。
- 列表的中點(diǎn)是「New Sublist」的尾部與「Old Sublist」的頭部相交的邊界。
- 當(dāng)
InnoDB
將頁(yè)面讀入 Buffer Pool 時(shí),它最初將其插入中點(diǎn)(「Old Sublist」的頭部)??梢宰x取頁(yè)面,因?yàn)樗枰糜谟脩舭l(fā)起的操作,如 SQL 查詢,或者作為InnoDB
自動(dòng)執(zhí)行的預(yù)讀操作的組成部分。 - 訪問(wèn)「Old Sublist」中的頁(yè)面數(shù)據(jù)會(huì)將其設(shè)置為“Young”,將其移動(dòng)到「New Sublist」的頭部。如果頁(yè)面是因?yàn)橛脩舭l(fā)起的操作而讀取的,則第一次訪問(wèn)立即發(fā)生,頁(yè)面變?yōu)椤癥oung”。如果頁(yè)面是因?yàn)轭A(yù)讀操作而讀取的,則第一次訪問(wèn)不會(huì)立即發(fā)生。
- 隨著數(shù)據(jù)庫(kù)的運(yùn)行,Buffer Pool 中未被訪問(wèn)的頁(yè)面會(huì)通過(guò)向列表尾部移動(dòng)而“老化”?!窷ew Sublist」和「Old Sublist」中的頁(yè)面都會(huì)隨著其他頁(yè)面變?yōu)樾马?yè)面而老化。當(dāng)頁(yè)面在中間插入時(shí),「Old Sublist」中的頁(yè)面也會(huì)老化。最終,一個(gè)未被使用的頁(yè)面會(huì)到達(dá)「Old Sublist」的尾部并被淘汰。
優(yōu)化提示:應(yīng)將緩沖池的大小設(shè)置為盡可能大的值,同時(shí)為服務(wù)器上其他進(jìn)程運(yùn)行留出足夠的內(nèi)存,避免過(guò)度分頁(yè)。緩沖池越大,
InnoDB
就越像內(nèi)存數(shù)據(jù)庫(kù),一次從磁盤讀取數(shù)據(jù),然后在后續(xù)讀取中從內(nèi)存中訪問(wèn)數(shù)據(jù)。
Change Buffer
"每秒 10 萬(wàn)次非主鍵更新,磁盤 IOPS 爆表!"運(yùn)維總監(jiān)癱坐在監(jiān)控屏前。林淵拔下服務(wù)器電源:"上 Change Buffer!"
實(shí)時(shí)監(jiān)控屏上,磁盤寫入曲線從鋸齒狀驟變?yōu)槠交本€:
Before: IOPS 15,000 → After: 2,300 (下降85%)
甲骨文特派員 Mike 臉色鐵青:"這算法...至少超越時(shí)代十年!”
林淵清了清嗓子,繼續(xù)給大家解釋 Change Buffer 的設(shè)計(jì)思路……
Change Buffer 是一種特殊的數(shù)據(jù)結(jié)構(gòu),針對(duì)非唯一二級(jí)索引的寫優(yōu)化結(jié)構(gòu),用于緩存當(dāng)二級(jí)索引頁(yè)不在 Buffer Pool 中時(shí)的寫操作。
當(dāng)二級(jí)索引頁(yè)不在緩沖池中時(shí),用于緩存對(duì)這些頁(yè)面的更改。這些由 INSERT
、 UPDATE
或 DELETE
操作(DML)產(chǎn)生的緩沖更改,將在頁(yè)面通過(guò)其他讀取操作加載到緩沖池時(shí)進(jìn)行合并。
- 若目標(biāo)頁(yè)不在 Buffer Pool,將變更記錄寫入 Change Buffer 生成 Redo Log 保證持久化;
- 當(dāng)后續(xù)讀取該索引頁(yè)時(shí),將 Change Buffer 中的變更合并(Merge)到 Buffer Pool,觸發(fā)異步刷盤。
圖片
臺(tái)下有人問(wèn):“有了 Buffer Pool 為何還要再設(shè)計(jì)一個(gè) Change Buffer 呢?”
與聚簇索引不同,二級(jí)索引通常是非唯一的,且對(duì)二級(jí)索引的插入操作往往以相對(duì)隨機(jī)的順序發(fā)生。
同樣,刪除和更新操作也可能影響索引樹中不相鄰的二級(jí)索引頁(yè)。
當(dāng)其他操作將受影響的頁(yè)讀入 Buffer Pool 時(shí),隨后將緩存的更改合并,避免了從磁盤讀取二級(jí)索引頁(yè)至 Buffer Pool 所需的大量隨機(jī)訪問(wèn) I/O。
系統(tǒng)在空閑時(shí)段或緩慢關(guān)閉期間運(yùn)行的清除操作會(huì)周期性地將更新的索引頁(yè)寫入磁盤。
相較于立即逐條寫入磁盤,清除操作能以更高效的方式批量寫入包含連續(xù)索引值的磁盤塊。
Change Buffer 有什么不足呢?
當(dāng)存在大量受影響的行和需要更新的二級(jí)索引時(shí),Change Buffer 合并可能需要數(shù)小時(shí)。
在此期間,磁盤 I/O 會(huì)增加,可能導(dǎo)致磁盤密集型查詢顯著變慢。Change Buffer 合并操作可能在事務(wù)提交后持續(xù)進(jìn)行,甚至在服務(wù)器關(guān)閉并重啟后仍會(huì)繼續(xù)。
在內(nèi)存中,Change Buffer 占用 Buffer Pool 的一部分空間。在磁盤上,Change Buffer 屬于系統(tǒng)表空間的一部分,當(dāng)數(shù)據(jù)庫(kù)服務(wù)器關(guān)閉時(shí),索引變更將在此處緩沖存儲(chǔ)。
Change Buffer 劃時(shí)代意義
當(dāng)對(duì)表執(zhí)行 INSERT
、 UPDATE
和 DELETE
操作時(shí),索引列的值(尤其是二級(jí)鍵的值)通常處于無(wú)序狀態(tài),需要大量 I/O 操作來(lái)更新二級(jí)索引。
當(dāng)相關(guān)頁(yè)面不在 Buffer Pool 中時(shí),Change Buffer 會(huì)緩存對(duì)二級(jí)索引條目的修改,從而避免立即從磁盤讀取頁(yè)面所產(chǎn)生的高昂 I/O 開銷。
當(dāng)頁(yè)面被加載到 Buffer Pool 時(shí),緩沖的更改會(huì)被合并,更新后的頁(yè)面隨后會(huì)刷寫到磁盤。
由于變更緩沖能夠減少磁盤讀寫次數(shù),因此對(duì)于 I/O 密集型工作負(fù)載(例如涉及大量 DML 操作的應(yīng)用場(chǎng)景,如批量插入)具有重要價(jià)值,這類場(chǎng)景可顯著受益于 Change Buffer 機(jī)制。
Adaptive Hash Index(自適應(yīng)哈希索引)
2005 年 eBay 中國(guó)競(jìng)標(biāo)現(xiàn)場(chǎng),林淵與 Oracle 團(tuán)隊(duì)正面對(duì)決。
"貴司方案處理不了熱點(diǎn)數(shù)據(jù)吧?"Oracle 首席亮出 TPC-C 測(cè)試報(bào)告。林淵輕笑一聲,敲下:
SET GLOBAL innodb_adaptive_hash_index=ON;
-- 激活哈希索引
瞬間,用戶 ID 查詢從 378ms 降至 0.09ms。
自適應(yīng)哈希索引(Adaptive Hash Index,AHI) 是 InnoDB 存儲(chǔ)引擎內(nèi)部自動(dòng)創(chuàng)建和管理的哈希索引,用于優(yōu)化 等值查詢(如 WHERE key = 'value'
) 的性能。
與傳統(tǒng)手動(dòng)創(chuàng)建的哈希索引不同,AHI 完全由 InnoDB 根據(jù)查詢模式動(dòng)態(tài)生成和銷毀,無(wú)需用戶干預(yù)。
核心作用:通過(guò)將頻繁訪問(wèn)的索引鍵值映射到哈希表,繞過(guò) B+ 樹的逐層查找,直接定位到目標(biāo)數(shù)據(jù)頁(yè),從而減少磁盤 I/O 和 CPU 開銷。
他的觸發(fā)條件是什么?
InnoDB 通過(guò)監(jiān)控索引頁(yè)的訪問(wèn)模式,動(dòng)態(tài)決定是否創(chuàng)建 AHI:
- 頻率閾值:同一索引頁(yè)被連續(xù)訪問(wèn)超過(guò)
100 次
。 - 查詢模式匹配:相同查詢條件多次訪問(wèn)同一頁(yè)(次數(shù)閾值:
頁(yè)中記錄數(shù) / 16
)。
生命周期管理
- 自動(dòng)創(chuàng)建:滿足觸發(fā)條件時(shí)動(dòng)態(tài)生成哈希條目。
- 自動(dòng)淘汰:
當(dāng)索引頁(yè)不再被頻繁訪問(wèn)時(shí),通過(guò) LRU 機(jī)制逐步移除哈希條目。
當(dāng)表被刪除或重建時(shí),相關(guān) AHI 條目自動(dòng)清理。
工作流程如下圖所示:
圖片
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn) | 局限性 |
減少等值查詢的 B+ 樹遍歷層級(jí) | 僅適用于等值查詢(=, IN),不適用范圍查詢 |
降低 CPU 和 I/O 開銷 | 哈希沖突可能影響性能 |
完全自動(dòng)化,無(wú)需人工維護(hù) | 高并發(fā)場(chǎng)景可能因鎖爭(zhēng)用成為瓶頸 |
對(duì)熱點(diǎn)數(shù)據(jù)訪問(wèn)有顯著加速效果 | 內(nèi)存占用增加(需權(quán)衡 |
使用場(chǎng)景建議
- 推薦開啟: OLTP 系統(tǒng)中以等值查詢?yōu)橹鞯膱?chǎng)景(如用戶中心、訂單查詢)。
- 建議關(guān)閉:
寫密集型負(fù)載(如日志寫入)。
內(nèi)存緊張或出現(xiàn)大量哈希沖突時(shí)。
使用 SSD 且 Buffer Pool 足夠大時(shí),B+ 樹自身性能已足夠。
Log Buffer(日志緩沖區(qū))
"林工,交易系統(tǒng)每秒百萬(wàn)事務(wù),如何保證零丟失?"
用 Redo Log 實(shí)現(xiàn)!避免每次事務(wù)操作都寫磁盤,我設(shè)計(jì)了 Log Buffer。
Log Buffer 是 InnoDB 存儲(chǔ)引擎用于臨時(shí)緩存 重做日志(Redo Log) 的內(nèi)存區(qū)域。
Log Buffer 大小由 innodb_log_buffer_size
變量定義,默認(rèn)大小為 64MB。
所有事務(wù)對(duì)數(shù)據(jù)的修改在寫入磁盤前,其對(duì)應(yīng)的 Redo Log 會(huì)先寫入 Log Buffer,隨后按策略批量刷新到磁盤的 Redo Log 文件中。
設(shè)計(jì)目標(biāo):
- 減少磁盤 I/O 次數(shù):合并多個(gè)日志寫入操作,避免頻繁的小數(shù)據(jù)量磁盤寫入。
- 提升事務(wù)響應(yīng)速度:延遲日志刷盤,降低事務(wù)提交的等待時(shí)間。
- 保證持久性(Durability):通過(guò)可控的刷盤策略,確保已提交事務(wù)的日志最終持久化。
Log Buffer 內(nèi)容會(huì)定期刷新到磁盤。較大的 Log Buffer 允許大型事務(wù)在提交前無(wú)需將 Redo Log 數(shù)據(jù)寫入磁盤。
因此,若有更新、插入或刪除大量行的事務(wù),增大 Log Buffer 可節(jié)省磁盤 I/O。
Log Buffer 實(shí)現(xiàn)原理
Log Buffer 內(nèi)存結(jié)構(gòu)與寫入流程:
- 日志生成:
- 事務(wù)修改數(shù)據(jù)頁(yè)時(shí),生成 Redo Log 記錄。
- 日志記錄包含修改內(nèi)容、LSN(Log Sequence Number)等信息。
- 緩沖區(qū)寫入:
Log Buffer 空間足夠,則日志按順序追加到 Log Buffer 的空閑位置。
使用
buf_free
指針標(biāo)記當(dāng)前寫入偏移量。
刷盤觸發(fā)條件:
事務(wù)提交:根據(jù)
innodb_flush_log_at_trx_commit
設(shè)置決定是否刷盤。緩沖區(qū)滿:當(dāng)寫入數(shù)據(jù)超過(guò)緩沖區(qū)空閑空間時(shí)強(qiáng)制刷盤。
定時(shí)任務(wù):每隔
innodb_flush_log_at_timeout
秒觸發(fā)刷盤(策略為 0 或 2 時(shí))。
圖片
刷盤策略詳解
參數(shù)值 | 行為描述 | 數(shù)據(jù)安全性 | 性能 | 適用場(chǎng)景 |
0 | 日志每秒刷盤一次,事務(wù)提交時(shí)不強(qiáng)制刷盤 | 最低 | 最高 | 非關(guān)鍵數(shù)據(jù)批量處理 |
1 | 每次事務(wù)提交時(shí)同步刷盤(fsync) | 最高 | 最低 | 金融交易等高安全需求 |
2 | 事務(wù)提交時(shí)寫入操作系統(tǒng)緩存,不立即刷盤;每秒由操作系統(tǒng)異步刷盤 | 中等 | 較高 | 常規(guī)業(yè)務(wù)系統(tǒng) |
Log Buffer 與 Redo Log 協(xié)作
1. 與 Redo Log 文件的關(guān)系
- 循環(huán)寫入:Redo Log 文件(如
ib_logfile0
、ib_logfile1
)以循環(huán)方式復(fù)用。 - LSN 協(xié)調(diào):
每個(gè)日志記錄攜帶 LSN,全局唯一且遞增。
Checkpoint LSN 標(biāo)記已持久化的日志位置。
2. 崩潰恢復(fù)流程
圖片
- 重啟時(shí)掃描 Redo Log:從最后一個(gè) Checkpoint LSN 開始重放日志。
- 前滾(Redo):將 Log Buffer 中未刷盤的日志重新應(yīng)用到數(shù)據(jù)頁(yè)。
- 后滾(Undo):通過(guò) Undo Log 回滾未提交的事務(wù)。
3. Group Commit 優(yōu)化
圖片
- 合并提交:多個(gè)事務(wù)的日志寫入合并為一次磁盤操作。
- 工作流程:
- 事務(wù)提交時(shí),將日志追加到 Log Buffer。
- 由后臺(tái)線程統(tǒng)一將多個(gè)事務(wù)的日志批量寫入磁盤。
- 減少頻繁的
fsync
調(diào)用,提升高并發(fā)下的吞吐量。
Log Buffer 通過(guò)內(nèi)存緩沖和批量刷盤機(jī)制,在 事務(wù)持久性 與 系統(tǒng)性能 之間取得平衡。
合理配置 innodb_flush_log_at_trx_commit
和緩沖區(qū)大小,結(jié)合 Group Commit 等優(yōu)化技術(shù),可顯著提升高并發(fā)場(chǎng)景下的數(shù)據(jù)庫(kù)性能。
同時(shí),需根據(jù)業(yè)務(wù)容忍度選擇恰當(dāng)?shù)乃⒈P策略,避免數(shù)據(jù)丟失風(fēng)險(xiǎn)。