《MySQL 性能優(yōu)化》之 InnoDB 存儲引擎

作者: 不剪發(fā)的Tony老師
畢業(yè)于北京航空航天大學(xué),十多年數(shù)據(jù)庫管理與開發(fā)經(jīng)驗(yàn),目前在一家全球性的金融公司從事數(shù)據(jù)庫架構(gòu)設(shè)計(jì)。CSDN學(xué)院簽約講師以及GitChat專欄作者。csdn上的博客收藏于以下地址:https://tonydong.blog.csdn.net


文章目錄

            InnoDB 概述
            InnoDB 系統(tǒng)結(jié)構(gòu)
            InnoDB 內(nèi)存結(jié)構(gòu)
                緩沖池
                變更緩沖
                日志緩沖
                自適應(yīng)哈希索引
            InnoDB 磁盤結(jié)構(gòu)
                表空間
                表和索引
                雙寫緩沖
                重做日志
                回滾日志

上一篇我們介紹了 MySQL 服務(wù)器的體系結(jié)構(gòu),其中插件式存儲引擎是 MySQL 與其他數(shù)據(jù)庫管理系統(tǒng)的最大區(qū)別。InnoDB 作為 MySQL 默認(rèn)的存儲引擎應(yīng)用最為廣泛;因此,本篇我們來介紹一下 InnoDB 存儲引擎。
InnoDB 概述

InnoDB 是一個(gè)具有高可靠性和高性能的通用存儲引擎,也是 MySQL 5.5 之后的默認(rèn)存儲引擎。因此,如果CREATE TABLE語句沒有指定ENGINE選項(xiàng),默認(rèn)創(chuàng)建的就是 InnoDB 表。

    ??使用SHOW VARIABLES LIKE 'default_storage_engine';命令可以查看默認(rèn)的存儲引擎。

在進(jìn)一步討論 InnoDB 體系結(jié)構(gòu)之前,我們先介紹幾個(gè) InnoDB 存儲引擎的關(guān)鍵特性:

    InnoDB 表的數(shù)據(jù)修改操作(DML)具有事務(wù)安全性(ACID),支持事務(wù)提交、事務(wù)回滾以及故障恢復(fù),能夠保障數(shù)據(jù)的一致性和完整性;
    InnoDB 采用更細(xì)粒度的行級鎖和類似 Oracle 的一致性讀(MVCC),能夠提高并發(fā)性和性能。
    InnoDB 按照主鍵索引(clustered index)的順序組織表中的數(shù)據(jù),優(yōu)化了基于主鍵字段的查詢。
    InnoDB 支持外鍵約束(FOREIGN KEY),能夠維護(hù)多個(gè)表之間的數(shù)據(jù)完整性。

當(dāng)然,InnoDB 存儲引擎提供的功能遠(yuǎn)遠(yuǎn)不止與此;正是由于這些強(qiáng)大的功能,使得 MySQL 能夠像 Oracle、Microsoft SQL Server 等商業(yè)數(shù)據(jù)庫一樣大量應(yīng)用在企業(yè)系統(tǒng)中。
InnoDB 系統(tǒng)結(jié)構(gòu)

下圖顯示了 InnoDB 存儲引擎的內(nèi)存結(jié)構(gòu)和磁盤結(jié)構(gòu)。


記住這張圖可以幫助我們理解 InnoDB 的體系結(jié)構(gòu),接下來我們分別討論 InnoDB 的內(nèi)存結(jié)構(gòu)和磁盤結(jié)構(gòu)。
InnoDB 內(nèi)存結(jié)構(gòu)

InnoDB 提供了自己的內(nèi)存組件,主要包括緩沖池(Buffer Pool)、變更緩沖(Change Buffer)、日志緩沖(Log Buffer)以及自適應(yīng)哈希索引(Adaptive Hash Index)技術(shù)。
緩沖池

緩沖池是 InnoDB 在內(nèi)存中的一個(gè)緩沖區(qū)域,主要用于緩存訪問過的表和索引等數(shù)據(jù)。緩沖池利用內(nèi)存直接處理數(shù)據(jù),避免磁盤操作,從而加快了數(shù)據(jù)處理的速度。

    ??在專用的 MySQL 服務(wù)器上,通常會給緩沖池分配多達(dá) 80% 的物理內(nèi)存。

以下命令顯示了 InnoDB 緩沖池相關(guān)的配置:

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name                       | Value          |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size       | 8388608        |
| innodb_buffer_pool_dump_at_shutdown | ON             |
| innodb_buffer_pool_dump_now         | OFF            |
| innodb_buffer_pool_dump_pct         | 25             |
| innodb_buffer_pool_filename         | ib_buffer_pool |
| innodb_buffer_pool_in_core_file     | ON             |
| innodb_buffer_pool_instances        | 1              |
| innodb_buffer_pool_load_abort       | OFF            |
| innodb_buffer_pool_load_at_startup  | ON             |
| innodb_buffer_pool_load_now         | OFF            |
| innodb_buffer_pool_size             | 8388608        |
+-------------------------------------+----------------+
11 rows in set (0.00 sec)

 

其中,innodb_buffer_pool_chunk_size 表示每個(gè)緩沖塊的大??;innodb_buffer_pool_instances 表示緩沖池的實(shí)例個(gè)數(shù),每個(gè)實(shí)例由數(shù)量相同的緩沖塊組成;innodb_buffer_pool_size 表示總的緩沖池大小,是 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 的倍數(shù)。

緩沖池管理算法

為了提高大量讀取操作時(shí)的效率,緩沖池被劃分為頁(page),每個(gè)頁可能包含多行數(shù)據(jù)。為了提高緩存管理的效率,緩沖池被實(shí)現(xiàn)為頁組成的鏈接列表。最終緩沖池使用特定的 LRU(最近最少使用)算法進(jìn)行管理,從而將頻繁訪問的數(shù)據(jù)保留在緩存中,將最少使用的緩存頁移除。

下圖演示了緩沖池管理的 LRU 算法。


InnoDB 使用 LRU 算法略有改動,緩存池被分為兩個(gè)部分:頭部的 5/8 是最近被訪問過的一個(gè)新的子列表,尾部的 3/8 是最近較少訪問的一個(gè)舊的子列表。這個(gè)比例由系統(tǒng)變量 innodb_old_blocks_pct 控制:

mysql> show variables like 'innodb_old_blocks_pct';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37    |
+-----------------------+-------+
1 row in set (0.00 sec)

 

當(dāng)一個(gè)新的頁需要被緩存時(shí),最近最少使用的頁將被剔除,新頁將被放入緩存池的新舊子列表的中間;這種方式被稱為中間點(diǎn)插入策略(midpoint insertion strategy)。用戶提交的操作(例如 SQL 查詢)或者 InnoDB 的預(yù)讀(read-ahead)操作都會導(dǎo)致新頁的緩存。

一方面,訪問舊子列表中的頁將會使得它被移動到新子列表的頭部,變得更新。如果是用戶操作引起的訪問,該頁將會立即被移動到新的子列表中;如果是預(yù)讀操作引起的訪問,不會立即導(dǎo)致移動,也可能根本不會移動。

另一方面,沒有被訪問的緩存頁將會逐漸被移動到列表的尾部,變得更舊。新子列表和舊子列表中的頁都會隨著其他頁的前移變得更舊;舊子列表中的頁還會隨著新頁的加入變得更舊,最終到達(dá)列表的最尾部并且被剔除。

我們可以輸入SHOW ENGINE INNODB STATUS命令,利用 InnoDB 標(biāo)準(zhǔn)監(jiān)控輸出查看緩沖池的使用指標(biāo),相關(guān)信息顯示在 BUFFER POOL AND MEMORY 部分:

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 8585216
Dictionary memory allocated 380485
Buffer pool size   512
Free buffers       249
Database pages     259
Old database pages 0
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 997, created 142, written 156
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 259, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

 

其中的 Buffer pool size 是緩沖池分配的數(shù)據(jù)頁數(shù)量(512),乘以 innodb_page_size(16384)等于緩沖池的大小(8388608)。

    ??關(guān)于 Buffer Pool 的配置和優(yōu)化我們將會在 MySQL 實(shí)例優(yōu)化的部分進(jìn)行介紹。

變更緩沖

變更緩沖緩存了那些不在緩沖池中的二級索引(secondary index)頁的修改操作。INSERT、UPDATE或者DELETE操作導(dǎo)致的變更將會在此緩沖,隨后再合并(由其他讀取操作引起)到緩沖池中。下圖演示了變更緩沖的作用過程。


與聚集索引(clustered index)不同,二級索引通常是非唯一索引,索引的插入、更新、刪除通常是順序隨機(jī)的操作。將變更進(jìn)行緩存,并且在隨后讀入緩沖池時(shí)進(jìn)行合并,能夠避免將輔助索引頁從磁盤讀入緩沖池所需的大量隨機(jī) I/O。

當(dāng)系統(tǒng)處于空閑狀態(tài)或在緩慢關(guān)閉期間運(yùn)行清除操作,定期將更新后的索引頁寫入磁盤。相對于每次將數(shù)據(jù)即寫入磁盤,這種清除操作可以更有效地寫入多個(gè)連續(xù)的索引值。

在內(nèi)存中,變更緩沖屬于緩沖池的一部分。在磁盤上,變更緩沖屬于系統(tǒng)表空間的一部分;當(dāng)數(shù)據(jù)庫服務(wù)器關(guān)閉時(shí),索引變更將會被緩沖到磁盤中。

系統(tǒng)變量 innodb_change_buffering 決定了何種類型的操作會被緩沖,默認(rèn)為 ALL。

如果索引中包含降序索引列或主鍵中包含降序索引列,就不會對二級索引進(jìn)行變更緩沖。

我們同樣可以輸入SHOW ENGINE INNODB STATUS命令,利用 InnoDB 標(biāo)準(zhǔn)監(jiān)控輸出查看變更緩沖的狀態(tài)信息:

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 1 buffer(s)
Hash table size 2267, node heap has 3 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s

 

因?yàn)樽兏彌_最開始只支持插入操作,所以顯示為 INSERT BUFFER AND ADAPTIVE HASH INDEX。

    ??關(guān)于 Change Buffer 的配置和優(yōu)化我們將會在 MySQL 實(shí)例優(yōu)化的部分進(jìn)行介紹。

日志緩沖

日志緩沖是重做日志(Redo Log)的內(nèi)存緩沖,日志緩沖的大小由變量 innodb_log_buffer_size 決定,默認(rèn)為 16 MB。日志緩沖的內(nèi)容會定期刷新到磁盤文件。設(shè)置一個(gè)大的日志緩沖使得大型事務(wù)不必在提交之前將重做日志數(shù)據(jù)寫入磁盤。因此,如果存在需要更新、插入或者刪除大量數(shù)據(jù)的事務(wù),可以通過增加日志緩沖的大小減少磁盤 I/O。

系統(tǒng)變量 innodb_flush_log_at_trx_commit 用于控制日志緩沖寫入磁盤的方式。默認(rèn)值為 1,即每次事務(wù)提交都會刷新緩沖到磁盤,滿足 ACID 特性。

系統(tǒng)變量 innodb_flush_log_at_timeout 用于控制日志緩沖刷新到磁盤的頻率。默認(rèn)值為 1 秒,即每隔 1 秒刷新一次。

    ??關(guān)于 Log Buffer 的配置和優(yōu)化我們將會在 MySQL 實(shí)例優(yōu)化的部分進(jìn)行介紹。

自適應(yīng)哈希索引

InnoDB 包含了一個(gè)監(jiān)控索引查找的機(jī)制,當(dāng) InnoDB 發(fā)現(xiàn)哈希索引可以提高查詢的性能時(shí)會自動創(chuàng)建哈希索引。哈希索引基于索引鍵的一個(gè)前綴部分創(chuàng)建,可能只包含了 B+樹索引中的一些值,通常時(shí)頻繁訪問的索引頁。

當(dāng)一個(gè)表能夠差不多完全加載到內(nèi)存中,哈希索引可以直接定位到所有數(shù)據(jù),因此能夠提高查詢性能。自適應(yīng)哈希索引特性由變量 innodb_adaptive_hash_index 設(shè)置,默認(rèn)為 ON。但是由于它需要占用緩沖池的內(nèi)存,只能用于等值查詢,而且只在特定的情況下有效,因此 MySQL 5.6 開始建議關(guān)閉該選項(xiàng)。

我們可以利用SHOW ENGINE INNODB STATUS命令查看自適應(yīng)哈希索引的使用情況,相關(guān)的數(shù)據(jù)也顯示在 INSERT BUFFER AND ADAPTIVE HASH INDEX 部分。
InnoDB 磁盤結(jié)構(gòu)

InnoDB 提供的磁盤存儲組件主要包括表空間(Tablespace)、表(Table)、索引(Index)、重做日志(Redo Log)、回滾日志(Undo Logs)以及雙寫緩沖(Doublewrite Buffer)。
表空間

表空間是一個(gè)邏輯上的存儲概念,用于存儲數(shù)據(jù)表、索引、回滾(Undo)數(shù)據(jù)等。一個(gè)表空間對應(yīng)操作系統(tǒng)上的一個(gè)或者多個(gè)文件。從邏輯概念上來說,表空間又是由段(Segment)組成,段由區(qū)間(Extent)組成,區(qū)間由頁(Page)組成,頁最終由行(Row)組成。


一個(gè) InndoDB 表通常對應(yīng)一個(gè)數(shù)據(jù)段,而區(qū)間是磁盤分配的基本單位,頁(默認(rèn)為 16 KB)是 InndoDB 管理磁盤的最小單位,與操作系統(tǒng)的頁(通常是 4 KB)概念不同。

InnoDB 提供的表空間包括:系統(tǒng)表空間(System Tablespace)、獨(dú)立表空間(File-Per-Table Tablespaces)、通用表空間(General Tablespaces)、回滾表空間(Undo Tablespaces)以及臨時(shí)表空間(Temporary Tablespaces)。

系統(tǒng)表空間

系統(tǒng)表空間用于存儲雙寫緩沖和變更緩沖。如果創(chuàng)建表和索引時(shí)不使用獨(dú)立表空間或通用表空間,它們也會被存儲到系統(tǒng)表空間;不推薦這種做法。在 MySQL 8.0 之前,系統(tǒng)表空間中還包含了 InnoDB 數(shù)據(jù)字典信息;從 MySQL 8.0 開始, InnoDB 使用統(tǒng)一的 MySQL 數(shù)據(jù)字典存儲元數(shù)據(jù)。

系統(tǒng)表空間可以擁有一個(gè)或多個(gè)數(shù)據(jù)文件。默認(rèn)情況下在數(shù)據(jù)目錄中創(chuàng)建一個(gè)名為 ibdata1 的系統(tǒng)表空間數(shù)據(jù)文件。系統(tǒng)表空間數(shù)據(jù)文件的大小和數(shù)量由系統(tǒng)參數(shù) innodb_data_file_path 進(jìn)行控制。

獨(dú)立表空間

獨(dú)立表空間(File-Per-Table Tablespaces)用于存儲單個(gè) InnoDB 表的數(shù)據(jù)和索引,每個(gè)表空間在文件系統(tǒng)中對應(yīng)單個(gè)數(shù)據(jù)文件。舉例來說,如果我們?yōu)?test 數(shù)據(jù)庫創(chuàng)建一個(gè)表 t1,MySQL 會在數(shù)據(jù)目錄下的 test 子目錄中創(chuàng)建一個(gè)數(shù)據(jù)文件 t1.idb。

InnoDB 默認(rèn)使用獨(dú)立表空間創(chuàng)建表,可以使用系統(tǒng)變量 innodb_file_per_table 進(jìn)行控制。如果禁用該參數(shù),InnoDB 將會默認(rèn)在系統(tǒng)表空間中創(chuàng)建表。

通用表空間

通用表空間是一種共享的 InnoDB 表空間,可以供多個(gè)表和索引使用。通用表空間比獨(dú)立表空間具有更高的內(nèi)存利用率。MySQL 服務(wù)器將會緩存表空間的元數(shù)據(jù),包含多個(gè)表的通用表空間需要的內(nèi)存比多個(gè)獨(dú)立表空間更少。

通用表空間可以像獨(dú)立表空間一樣在 MySQL 數(shù)據(jù)目錄內(nèi)部或者外部創(chuàng)建數(shù)據(jù)文件,從而為關(guān)鍵的表指定單獨(dú)的存儲,例如 RAID 或者 DRBD,提高數(shù)據(jù)訪問的性能。

通用表空間使用 CREATE TABLESPACE 語句創(chuàng)建。

回滾表空間

回滾表空間用于存儲回滾日志,回滾日志記錄中包含了撤銷事務(wù)對聚集索引記錄所作的最新修改所需的信息?;貪L記錄存儲在回滾日志段中,回滾日志段存儲在回滾段中。系統(tǒng)變量 innodb_rollback_segments 決定了每個(gè)回滾表空間分配的回滾段數(shù)量。

MySQL 實(shí)例初始化時(shí)會創(chuàng)建兩個(gè)回滾表空間。 默認(rèn)的回滾表空間在 innodb_undo_directory 參數(shù)指定的目錄中創(chuàng)建,如果沒有定義該參數(shù),則在數(shù)據(jù)目錄中創(chuàng)建。默認(rèn)回滾表空間的數(shù)據(jù)文件名為 undo_001 和 undo_002,對應(yīng)數(shù)據(jù)字典中的回滾表空間名為 innodb_undo_001 和 innodb_undo_002。

從 MySQL 8.0.14 開始,可以使用 CREATE UNDO TABLESPACE 增加額外的回滾表空間;一個(gè) MySQL 實(shí)例最多可以存在 127 個(gè)回滾表空間,包括默認(rèn)的兩個(gè)回滾表空間。

臨時(shí)表空間

InnoDB 存在兩種臨時(shí)表空間:會話臨時(shí)表空間(session temporary tablespaces)和一個(gè)全局臨時(shí)表空間(global temporary tablespace)。

會話臨時(shí)表空間用于存儲用戶創(chuàng)建的臨時(shí)表;當(dāng) InnoDB 被設(shè)置為磁盤內(nèi)部臨時(shí)表的存儲引擎時(shí),會話臨時(shí)表空間也用于優(yōu)化器創(chuàng)建的內(nèi)部臨時(shí)表。從 MySQL 8.0.16 開始,磁盤內(nèi)部臨時(shí)表的存儲引擎永遠(yuǎn)都是 InnoDB;在此之前由參數(shù) internal_tmp_disk_storage_engine 決定 。

系統(tǒng)變量 innodb_temp_tablespaces_dir 決定了會話臨時(shí)表空間的文件目錄,默認(rèn)為數(shù)據(jù)目錄下的 #innodb_temp 子目錄。 表 INFORMATION_SCHEMA.INNODB_SESSION_TEMP_TABLESPACES 存儲了會話臨時(shí)表空間的元數(shù)據(jù),表 INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO 存儲了當(dāng)前活動的用戶臨時(shí)表的元數(shù)據(jù)。

全局臨時(shí)表空間(ibtmp1)存儲了用戶臨時(shí)表修改信息的回滾段數(shù)據(jù)。系統(tǒng)變量 innodb_temp_data_file_path 定義了全局臨時(shí)表空間數(shù)據(jù)文件的相對路徑、名稱、大小以及屬性。如果沒有指定該參數(shù),默認(rèn)在 innodb_data_home_dir 目錄中創(chuàng)建一個(gè)名為 ibtmp1 的自動擴(kuò)展的數(shù)據(jù)文件,初始大小略微大于 12 MB。
表和索引

表是數(shù)據(jù)庫中存儲數(shù)據(jù)的主要對象,使用CREATE TABLE語句創(chuàng)建。

CREATE TABLE t1 (a INT, b CHAR (20), PRIMARY KEY (a)) ENGINE=InnoDB;

 

其中,ENGINE 用于指定表的存儲類型;如果不指定,MySQL 默認(rèn)使用 InnoDB 存儲引擎。使用以下命令查看表的信息:

mysql> SHOW TABLE STATUS LIKE 't%' \G
*************************** 1. row ***************************
           Name: t1
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2020-02-17 14:32:40
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_0900_ai_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

 

InnoDB 表按照索引的組織方式存儲數(shù)據(jù),被稱為聚簇索引(clustered index)。具體來說,

    如果指定了 PRIMARY KEY,InnoDB 使用主鍵作為聚簇索引。推薦使用生成之后不會更改、不為空且不重復(fù)、經(jīng)常作為查詢條件的字段作為主鍵,例如各種編號。如果沒有邏輯上唯一且非空的字段,可以使用自增字段 AUTO_INCREMENT 作為主鍵。
    如果沒有定義 PRIMARY KEY,MySQL 使用第一個(gè)非空且唯一的索引字段作為 InnoDB 表的聚集索引。
    如果沒有定義 PRIMARY KEY 也沒有合適的 UNIQUE,InnoDB 會在內(nèi)部生成一個(gè) 行 ID 字段,并且創(chuàng)建一個(gè)隱藏的聚集索引 GEN_CLUST_INDEX 。InnoDB 為表中的每一行生成一個(gè)遞增的 ID 值,并且按照該順序存儲數(shù)據(jù)。

除了聚簇索引之外的索引被稱為二級索引(secondary indexes)。InnoDB 二級索引中的每個(gè)索引記錄都包含了主鍵索引列的值,以及二級索引的字段。InnoDB 使用主鍵值查找聚集索引中的數(shù)據(jù)行。因此,如果主鍵字段很長,二級索引就需要占用更多的磁盤空間,查找的效率就會更低。這也就是為什么 InnoDB 推薦使用簡單的數(shù)字作為主鍵。

    ??關(guān)于索引和優(yōu)化我們將會在 MySQL 模式優(yōu)化的部分進(jìn)行介紹。

雙寫緩沖

雙寫緩沖是系統(tǒng)表空間中的一個(gè)存儲區(qū)域;在 InnoDB 將緩沖池刷新到數(shù)據(jù)文件之前,會先將緩沖頁寫入該區(qū)域。如果在寫入數(shù)據(jù)頁的過程中,出現(xiàn)了操作系統(tǒng)、存儲系統(tǒng)或者 mysqld 進(jìn)程崩潰,InnoDB 可以利用雙寫緩沖存儲的緩沖頁進(jìn)行故障恢復(fù)。

由于 InnoDB 的數(shù)據(jù)頁大小往往和操作系統(tǒng)數(shù)據(jù)頁大小不一致,例如 InnoDB 為 16 KB,操作系統(tǒng)為 4 KB;此時(shí) InnoDB 刷新一個(gè)數(shù)據(jù)頁,操作系統(tǒng)需要刷新 4 個(gè)數(shù)據(jù)頁,在系統(tǒng)故障時(shí)可能只刷新了部分?jǐn)?shù)據(jù)頁。雙寫緩沖會先把緩沖池的數(shù)據(jù)寫入共享表空間,然后再刷新數(shù)據(jù)頁;如果在這個(gè)過程中發(fā)生系統(tǒng)崩潰,InnoDB 可以從共享表空間獲取到要刷新的數(shù)據(jù),然后重新執(zhí)行寫入。

雖然數(shù)據(jù)需要寫入兩次,雙寫緩沖并不會導(dǎo)致兩倍的 I/O 負(fù)載或者操作,因?yàn)殡p寫緩沖只需要寫入一個(gè)連續(xù)的數(shù)據(jù)塊,只有一次 fsync() 系統(tǒng)調(diào)用。

雙寫緩沖由系統(tǒng)變量 innodb_doublewrite 控制,默認(rèn)值為 ON。如果文件系統(tǒng)或者存儲設(shè)備提供了防止部分寫失效的功能,可以禁用雙寫緩沖。
重做日志

重做日志用于故障恢復(fù)時(shí)修復(fù)未完成事務(wù)的數(shù)據(jù),它位于磁盤中,與內(nèi)存中的日志緩沖相對應(yīng)。在正常操作過程中,重做日志記錄了表中的數(shù)據(jù)修改信息。當(dāng)系統(tǒng)出現(xiàn)異常關(guān)閉后,重新啟動時(shí)自動利用重做日志恢復(fù)未更新到數(shù)據(jù)文件中的修改。

默認(rèn)情況下,重做日志物理上由兩個(gè)文件 ib_logfile0 和 ib_logfile1 組成。MySQL 使用循環(huán)的方式寫入重做日志文件。
回滾日志

回滾日志由一組回滾日志記錄組成,這些記錄屬于單個(gè)讀寫事務(wù)?;貪L日志記錄包含了回滾一個(gè)事務(wù)對聚集索引記錄的最新修改所需的信息。另外,如果另一個(gè)事務(wù)需要查看原始的數(shù)據(jù)(一致性讀),將會從回滾日志記錄中返回未修改前的數(shù)據(jù)。

回滾日志存儲在回滾日志段中,后者包含在回滾段中;回滾段存儲在回滾表空間以及全局臨時(shí)表空間中。

下一篇我們將會討論 MySQL 中的事務(wù)管理與并發(fā)控制,歡迎關(guān)注??、評論??、點(diǎn)贊??!