從 MySQL 5.5
版本開始默認(rèn)使用 InnoDB
作為引擎做葵,它擅長處理事務(wù)酗洒,具有自動崩潰恢復(fù)的特性窗骑,在日常開發(fā)中使用非常廣泛。下面是官方的 InnoDB
引擎架構(gòu)圖耳鸯,主要分為內(nèi)存結(jié)構(gòu)和磁盤結(jié)構(gòu)兩大部分湿蛔。
1. InnoDB 內(nèi)存結(jié)構(gòu)
內(nèi)存結(jié)構(gòu)主要包括 Bu?er Pool
膀曾、Change Bu?er
、Adaptive Hash Index
和 Log Bu?er
四大組件阳啥。
SHOW ENGINE INNODB STATUS;
Database pages + Free buffers <= Buffer pool size
(其它空間有可能分配給自適應(yīng)索引和 changeBuffer
)
1.1 Buffer Pool
緩沖池添谊,簡稱 BP
。BP
以 Page
頁為單位苫纤,默認(rèn)大小 16K
碉钠,BP
的底層采用鏈表數(shù)據(jù)結(jié)構(gòu)管理 Page
。在 InnoDB
訪問表記錄和索引時會在 Page
頁中緩存卷拘,以后使用可以減少磁盤 IO
操作喊废,提升效率。
1.1.1 Page 管理機(jī)制
Page
根據(jù)狀態(tài)可以分為三種類型:
-
free page
: 空閑page
栗弟,未被使用 -
clean page
:被使用page
污筷,數(shù)據(jù)沒有被修改過 -
dirty page
:臟頁,被使用page
乍赫,數(shù)據(jù)被修改過瓣蛀,頁中數(shù)據(jù)和磁盤的數(shù)據(jù)產(chǎn)生了不一致
針對上述三種 page
類型,InnoDB
通過三種鏈表結(jié)構(gòu)來維護(hù)和管理
-
free list
:表示空閑緩沖區(qū)雷厂,管理free page
-
?ush list
:表示需要刷新到磁盤的緩沖區(qū)惋增,管理dirty page
,內(nèi)部page
按修改時間排序改鲫。臟頁既存在于?ush鏈表
诈皿,也在LRU鏈表
中,但是兩種互不影響像棘,LRU鏈表
負(fù)責(zé)管理page
的可用性和釋放稽亏,而?ush鏈表
負(fù)責(zé)管理臟頁的刷盤操作。 -
lru list
:表示正在使用的緩沖區(qū)缕题,管理clean page
和dirty page
截歉,緩沖區(qū)以midpoint
為基點,前面鏈表稱為new
列表區(qū)烟零,存放經(jīng)常訪問的數(shù)據(jù)瘪松,占63%
;后 面的鏈表稱為old
列表區(qū)瓶摆,存放使用較少數(shù)據(jù)凉逛,占37%
。
1.1.2 改進(jìn)型 LRU 算法維護(hù)
- 普通
LRU
:末尾淘汰法群井,新數(shù)據(jù)從鏈表頭部加入,釋放空間時從末尾淘汰 - 改進(jìn)
LRU
:鏈表分為new
和old
兩個部分毫胜,加入元素時并不是從表頭插入书斜,而是從中間midpoint
位置插入诬辈,如果數(shù)據(jù)很快被訪問,那么page
就會向new
列表頭部移動荐吉,如果數(shù)據(jù)沒有被訪問焙糟,會逐步向old
尾部移動,等待淘汰样屠。
每當(dāng)有新的 page
數(shù)據(jù)讀取到 buffer pool
時穿撮,InnoDB
引擎會判斷是否有空閑頁,是否足夠痪欲,如果有就將 free page
從 free list
列表刪除悦穿,放入到 LRU
列表中。沒有空閑頁业踢,就會根據(jù) LRU
算法淘汰 LRU
鏈表默認(rèn)的頁栗柒,將內(nèi)存空間釋放分配給新的頁。
1.1.3 Buffer Pool 配置參數(shù)
-- 查看page頁大小
show variables like '%innodb_page_size%';
-- 查看 lru list 中 old 列表參數(shù)
show variables like '%innodb_old%';
-- 查看 bu?er pool 參數(shù)
show variables like '%innodb_buffer%';
建議:將 innodb_buffer_pool_size
設(shè)置為總內(nèi)存大小的 60%-80%
知举, innodb_buffer_pool_instances
可以設(shè)置為多個瞬沦,這樣可以避免緩存爭奪。
1.2 Change Buffer
寫緩沖區(qū)雇锡,簡稱 CB
逛钻。在進(jìn)行 DML
操作時,如果 BP
沒有其相應(yīng)的 Page
數(shù)據(jù)锰提, 并不會立刻將磁盤頁加載到緩沖池曙痘,而是在 CB
記錄緩沖變更,等未來數(shù)據(jù)被讀取時欲账,再將數(shù)據(jù)合并恢復(fù)到 BP
中屡江。
ChangeBuffer
占用 BufferPool
空間,默認(rèn)占25%
赛不,大允許占50%
惩嘉,可以根據(jù)讀寫業(yè)務(wù)量來 進(jìn)行調(diào)整。參數(shù) innodb_change_buffer_max_size
;
show variables like '%innodb_change_buffer_max_size%';
當(dāng)更新一條記錄時踢故,該記錄在 BufferPool
存在文黎,直接在 BufferPool
修改,一次內(nèi)存操作殿较。如果該記錄在 BufferPool
不存在(沒有命中)耸峭,會直接在 ChangeBuffer
進(jìn)行一次內(nèi)存操作,不用再去磁盤查詢數(shù)據(jù)淋纲,避免一次磁盤IO
劳闹。當(dāng)下次查詢記錄時,會先進(jìn)性磁盤讀取,然后再從 ChangeBuffer
中讀取信息合并本涕,最終載入BufferPool
中业汰。
寫緩沖區(qū),僅適用于非唯一普通索引頁菩颖,為什么样漆?
如果在索引設(shè)置唯一性,在進(jìn)行修改時晦闰,InnoDB
必須要做唯一性校驗放祟,因此必須查詢磁盤, 做一次 IO
操作呻右。會直接將記錄查詢到 BufferPool
中跪妥,然后在緩沖池修改,不會在ChangeBuffer
操作窿冯。
1.3 Adaptive Hash Index
自適應(yīng)哈希索引骗奖,用于優(yōu)化對 BP
數(shù)據(jù)的查詢。InnoDB
存儲引擎會監(jiān) 控對表索引的查找醒串,如果觀察到建立哈希索引可以帶來速度的提升执桌,則建立哈希索引,所以稱之為自適應(yīng)芜赌。InnoDB
存儲引擎會自動根據(jù)訪問的頻率和模式來為某些頁建立哈希索引仰挣。
1.4 Log Bu?er
日志緩沖區(qū),用來保存要寫入磁盤上 log
文件(Redo/Undo
)的數(shù)據(jù)缠沈,日志緩沖區(qū)的內(nèi)容定期刷新到磁盤 log
文件中膘壶。日志緩沖區(qū)滿時會自動將其刷新到磁盤,當(dāng)遇到 BLOB
或多行更新的大事務(wù)操作時洲愤,增加日志緩沖區(qū)可以節(jié)省磁盤 I/O
颓芭。
LogBuffer
主要是用于記錄 InnoDB
引擎日志,在 DML
操作時會產(chǎn)生 Redo
和Undo
日志柬赐。LogBuffer
空間滿了亡问,會自動寫入磁盤「厮危可以通過將 innodb_log_buffer_size
參數(shù)調(diào)大州藕,減少磁盤IO
頻率
show variables like '%innodb_log_buffer_size%';
show variables like '%innodb_log%';
SHOW VARIABLES LIKE '%innodb_flush_log_at_trx_commit%'
innodb_flush_log_at_trx_commit
參數(shù)控制日志刷新行為,默認(rèn)為1
-
0 :
每隔1
秒寫日志文件和刷盤操作(寫日志文件LogBu?er -> OS cache
酝陈,刷盤OS cache -> 磁盤文件
)床玻,最多丟失1
秒數(shù)據(jù) -
1:
事務(wù)提交,立刻寫日志文件和刷盤沉帮,數(shù)據(jù)不丟失锈死,但是會頻繁IO
操作 -
2:
事務(wù)提交贫堰,立刻寫日志文件,每隔1
秒鐘進(jìn)行刷盤操作
2. 磁盤結(jié)構(gòu)
InnoDB
磁盤主要包含Tablespaces
馅精,InnoDB Data Dictionary
严嗜,Doublewrite Buffer
粱檀、Redo Log
和 Undo Logs
洲敢。
2.1 表空間(Tablespaces)
用于存儲表結(jié)構(gòu)和數(shù)據(jù)。表空間又分為系統(tǒng)表空間茄蚯、獨立表空間压彭、 通用表空間、臨時表空間渗常、Undo
表空間等多種類型壮不;
2.1.1 系統(tǒng)表空間(The System Tablespace)
包含 InnoDB
數(shù)據(jù)字典,Doublewrite Buffer
皱碘,Change Bu?er
询一,Undo Logs
的存儲區(qū)域。系統(tǒng)表空間也默認(rèn)包含任何用戶在系統(tǒng)表空間創(chuàng)建的表數(shù)據(jù)和索引數(shù)據(jù)癌椿。系統(tǒng)表空間是一個共享的表空間因為它是被多個表共享的健蕊。該空間的數(shù)據(jù)文件通過參數(shù) innodb_data_?le_path
控制,默認(rèn)值是 ibdata1:12M:autoextend
(文件名為ibdata1
踢俄、 12MB
缩功、自動擴(kuò)展)。
SHOW VARIABLES LIKE '%innodb_data_file_path%'
2.1.2 獨立表空間(File-Per-Table Tablespaces)
默認(rèn)開啟都办,獨立表空間是一個單表表空間嫡锌,該表創(chuàng)建于自己的數(shù)據(jù)文件中,而非創(chuàng)建于系統(tǒng)表空間中琳钉。當(dāng) innodb_?le_per_table
選項開啟時势木,表將被創(chuàng)建于表空間中。否則歌懒, innodb
將被創(chuàng)建于系統(tǒng)表空間中啦桌。每個表文件表空間由一個 .ibd
數(shù)據(jù)文件代表,該文件默認(rèn)被創(chuàng)建于數(shù)據(jù)庫目錄中歼培。表空間的表文件支持動態(tài)(dynamic
)和壓縮 (commpressed
)行格式震蒋。
SHOW VARIABLES LIKE '%innodb_file_per_table%'
2.1.3 通用表空間(General Tablespaces)
通用表空間為通過 create tablespace
語法創(chuàng)建的共享表空間。通用表空間可以創(chuàng)建于 mysql
數(shù)據(jù)目錄外的其他表空間躲庄,其可以容納多張表查剖,且其支持所有的行格式。
-- 創(chuàng)建表空 間ts1
CREATE TABLESPACE ts1 ADD DATAFILE ts1.ibd Engine=InnoDB;
-- 將表添加到 ts1 表空間
CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1;
2.1.4 撤銷表空間(Undo Tablespaces)
撤銷表空間由一個或多個包含 Undo
日志文件組成噪窘。在 MySQL 5.7
版本之前Undo
占用的 是System Tablespace
共享區(qū)笋庄,從5.7
開始將Undo
從 System Tablespace
分離了出來。 InnoDB
使用的 undo
表空間由 innodb_undo_tablespaces
配置選項控制,默認(rèn)為0
直砂。參 數(shù)值為0
表示使用系統(tǒng)表空間ibdata1
菌仁;大于0
表示使用undo
表空間undo_001、 undo_002
等静暂。
SHOW VARIABLES LIKE '%innodb_undo_tablespaces%'
2.1.5 臨時表空間(Temporary Tablespaces)
分為 session temporary tablespaces
和 global temporary tablespace
兩種济丘。session temporary tablespaces
存儲的是用戶創(chuàng)建的臨時表和磁盤內(nèi)部的臨時表庆揪。global temporary tablespace
儲存用戶臨時表的回滾段(rollback segments
)煞抬。mysql
服務(wù)器正常關(guān)閉或異常終止時夏跷,臨時表空間將被移除鹤耍,每次啟動時會被重新創(chuàng)建恨溜。
2.2 數(shù)據(jù)字典(InnoDB Data Dictionary)
InnoDB
數(shù)據(jù)字典由內(nèi)部系統(tǒng)表組成昭齐,這些表包含用于查找表瞎惫、索引和表字段等對象的元數(shù) 據(jù)盐股。元數(shù)據(jù)物理上位于InnoDB
系統(tǒng)表空間中驮审。由于歷史原因鲫寄,數(shù)據(jù)字典元數(shù)據(jù)在一定程度上 與InnoDB
表元數(shù)據(jù)文件(.frm
文件)中存儲的信息重疊。
2.3 雙寫緩沖區(qū)(Doublewrite Buffer)
位于系統(tǒng)表空間疯淫,是一個存儲區(qū)域地来。在 BufferPage
的 page
頁刷新到磁盤真正的位置前,會先將數(shù)據(jù)存在Doublewrite
緩沖區(qū)峡竣。如果在 page
頁寫入過程中出現(xiàn)操作系統(tǒng)靠抑、存儲子系統(tǒng)或 mysqld
進(jìn)程崩潰,InnoDB
可以在崩潰恢復(fù)期間從Doublewrite
緩沖區(qū)中找到頁的一個備份适掰。在大多數(shù)情況下颂碧,默認(rèn)情況下啟用雙寫緩沖區(qū);要禁用 Doublewrite
緩沖區(qū)类浪,可以將 innodb_doublewrite
設(shè)置為0
载城。使用Doublewrite
緩沖區(qū)時建議將 innodb_?ush_method
設(shè)置為 O_DIRECT
。
SHOW VARIABLES LIKE '%innodb_doublewrite%'
SHOW VARIABLES LIKE '%innodb_flush_method%'
MySQL 的 innodb_?ush_method 這個參數(shù)控制著 innodb 數(shù)據(jù)文件及 redo log 的打開费就、 刷寫模式诉瓦。有三個值:fdatasync(默認(rèn)),O_DSYNC力细,O_DIRECT睬澡。
設(shè)置 O_DIRECT 表示數(shù)據(jù)文件寫入操作會通知操作系統(tǒng)不要緩存數(shù)據(jù),也不要用預(yù)讀眠蚂,直接從Innodb Buffer 寫到磁盤文件煞聪。
默認(rèn)的 fdatasync 意思是先寫入操作系統(tǒng)緩存,然后再調(diào)用 fsync() 函數(shù)去異步刷數(shù)據(jù)文件與redo log 的緩存信息逝慧。
2.4 重做日志(Redo Log)
重做日志是一種基于磁盤的數(shù)據(jù)結(jié)構(gòu)昔脯,用于在崩潰恢復(fù)期間更正不完整事務(wù)寫入的數(shù)據(jù)啄糙。 MySQL
以循環(huán)方式寫入重做日志文件,記錄 InnoDB
中所有對 Buffer Pool
修改的日志云稚。當(dāng)出現(xiàn)實例故障(像斷電)隧饼,導(dǎo)致數(shù)據(jù)未能更新到數(shù)據(jù)文件,則數(shù)據(jù)庫重啟時須 redo
静陈,重新把數(shù)據(jù)更新到數(shù)據(jù)文件燕雁。讀寫事務(wù)在執(zhí)行的過程中,都會不斷的產(chǎn)生 redo log
窿给。默認(rèn)情況下贵白,重做日志在磁盤上由兩個名為 ib_log?le0
和 ib_log?le1
的文件物理表示。
2.5 撤銷日志(Undo Logs)
撤消日志是在事務(wù)開始之前保存的被修改數(shù)據(jù)的備份崩泡,用于例外情況時回滾事務(wù)。撤消日志屬于邏輯日志猬膨,根據(jù)每行記錄進(jìn)行記錄角撞。撤消日志存在于系統(tǒng)表空間、撤消表空間和臨時表空間中勃痴。
3. 新版本結(jié)構(gòu)演變
3.1 MySQL 5.7 版本
- 將
Undo
日志表空間從共享表空間ibdata
文件中分離出來谒所,可以在安裝MySQL
時由用戶自行指定文件大小和數(shù)量。 - 增加了
temporary
臨時表空間沛申,里面存儲著臨時表或臨時查詢結(jié)果集的數(shù)據(jù)劣领。 -
Buffer Pool
大小可以動態(tài)修改,無需重啟數(shù)據(jù)庫實例铁材。
3.2 MySQL 8.0 版本
- 將
InnoDB
表的數(shù)據(jù)字典和Undo
都從共享表空間ibdata
中徹底分離出來了尖淘,以前需要ibdata
中數(shù)據(jù)字典與獨立表空間ibd
文件中數(shù)據(jù)字典一致才行,8.0
版本就不需要了著觉。 -
temporary
臨時表空間也可以配置多個物理文件村生,而且均為InnoDB
存儲引擎并能創(chuàng)建索引,這樣加快了處理的速度饼丘。 - 用戶可以像
Oracle
數(shù)據(jù)庫那樣設(shè)置一些表空間趁桃,每個表空間對應(yīng)多個物理文件,每個表空間可以給多個表使用肄鸽,但一個表只能存儲在一個表空間中卫病。 - 將
Doublewrite Buffer
從共享表空間ibdata
中也分離出來了。