MySQL Redo Log
MySQL主從復制:https://blog.csdn.net/a18792721831/article/details/146117935
MySQL Binlog:https://blog.csdn.net/a18792721831/article/details/146606305
MySQL General Log:https://blog.csdn.net/a18792721831/article/details/146607343
MySQL Slow Log:https://blog.csdn.net/a18792721831/article/details/147166971
MySQL Error Log:https://blog.csdn.net/a18792721831/article/details/147167038
MySQL Redo Log: https://blog.csdn.net/a18792721831/article/details/149862528
MySQL Undo Log: https://blog.csdn.net/a18792721831/article/details/149880355
MySQL Redo Log
- Redo Log 介紹
- Redo Log 的落盤
- Redo Log 的數量及大小修改
- CheckPoint
- LSN
- Mysql 8.0 的 Redo Log 歸檔
- Mysql 8.0 中的 Redo Log 禁用
- 官網文檔
InnoDB使用 Redo Log 來保證數據的一致性和可持久化。
Redo Log 介紹
MySQL采用的是WAL(Write-Ahead Logging)技術,也就是先寫日志在寫磁盤。如果有修改操作,則先將操作記錄在 Redo Log 中,然后將 Redo Log Buffer 中的數據刷新到磁盤的日志文件中,最后寫入數據文件中。
Redo Log 記錄了這個頁做了什么改動,對Redo Log 進行落盤,不僅可以保證數據持久化,還可以提高寫入效率。
- MySQL如果每次都按頁落盤,即使有少量數據的修改,每次落盤也是整個數據頁,那么IO成本將會非常高。而Redo Log 記錄的是緩沖池中頁的修改記錄,因此每次只需要對 Redo Log 進行落盤,而不需要對整個頁落盤。大大降低了落盤的數據量,節約成本,提高寫入效率。
- 如果每次都是將頁落盤,那么頁會在不同的位置,因此落盤時基本上就是隨機IO,而有了Redo Log,只將Redo Log 落盤,就可以將這些隨機 IO 轉換成順序IO ,這樣也可以提高寫入效率。
Redo Log 另一個很重要的作用就是崩潰恢復能力。事務在提交時會把Redo Log 刷新到磁盤中,如果此時系統宕機了,那么重啟之后,只需要根據 Redo Log 的記錄把數據頁未更新的部分更新一下,就可以恢復事務所做的數據變更。
Redo Log 的落盤
Redo Log 由兩部分組成: Redo Log 緩沖區和 Redo Log 文件。
Redo Log 緩沖區的大小通過 innodb_log_buffer_size
參數來設置。通過 show global variables like 'innodb_log_buffer_size';
查看
16777216B=16384K=16M
當事務更新時,一般先寫入Redo Log 緩沖區,在寫入 Redo Log 文件。而具體的寫入頻率由innodb_flush_log_at_trx_commit
參數控制。查看 show global variables like 'innodb_flush_log_at_trx_commit';
innodb_flush_log_at_trx_commit
參數有3個有效值:
- 0,每秒將日志緩沖區寫入日志文件一次,并在日志文件上執行磁盤刷新操作,未刷新日志的事務可能會在崩潰中丟失,此時 InnoDB不在符合事務持久性的要求。
- 1,在每次提交事務時,日志緩沖區都會寫入日志文件中,并在日志文件上執行磁盤刷新操作。
- 2,在每次提交事務后寫入日志,并且日志每秒刷新一次到磁盤。未刷新日志的事務可能會在崩潰中丟失。當MySQL服務發生宕機,但操作系統沒有發生宕機時,不會出現數據丟失。但是當操作系統宕機時,重啟后可能會丟失Redo Log 緩沖中還沒有刷新到 Redo Log 文件中的數據。
選擇不同的落盤頻率,對于MySQL的性能存在一定的影響
創建一張表,用于驗證性能
use test;
drop table if exists test_redo;
create table `test_redo`(
`id` int not null auto_increment,
`a` varchar(20) default null,
`b` int default null,
`c` datetime not null default current_timestamp,
primary key (`id`)
) engine=innodb charset=utf8mb4;
drop procedure if exists test_insert;
delimiter ;;
create procedure test_insert()
begin
declare i int;
set i = 1;
while(i <= 100000) do
insert into test_redo(a,b) values(i,i);
set i=i+1;
end while;
end;;
delimiter ;
執行
執行**set global innodb_flush_log_at_trx_commit=0
**后,調用 test_insert
插入10萬的數據
花費了3分27秒5,也就是207.5秒。
執行**set global innodb_flush_log_at_trx_commit=1
**,調用test_insert
插入10萬數據
花費了6分55秒4,也就是415.4秒,是等于0的2倍
執行**set global innodb_flush_log_at_trx_commit=2
**,調用test_insert
插入10萬數據
和等于0差不多。
innodb_flush_log_at_trx_commit
參數設置為0或2的寫入速度比設置為1的寫入速度快很多。但是如果某個MySQL實例將 innodb_flush_log_at_trx_commit
參數設置為0或2,那么它其實已經不具備ACID中的D(Durability,持久性)。在一般情況下,建議將innodb_flush_log_at_trx_commit
參數設置為1,這樣可以保證MySQL在崩潰恢復時數據不丟失。
Redo Log 的數量及大小修改
MySQL啟動后,Redo Log 的大小和個數就是固定的,比如可以配置3個大小為2GB的Redo Log,文件名類似于 ib_logfile0
。Redo Log 是循環寫的。
從 MySQL 8.0.30 開始,支持“多文件”redo log,文件名格式為:#ib_redo<N>
https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-redo-log-multiple-files
從MySQL 從 8.0.30 版本(2022年7月發布)開始,對 InnoDB 的 redo log 存儲方式進行了重大調整,將傳統的 ib_logfile0
、ib_logfile1
文件改為存儲在 #innodb_redo
目錄中
https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html
帶有
_tmp
是備用redo_log。因為在 mysql 8.0 中增加了動態調整 redo_log大小的能力,在調整過程中,會使用備用redo_log,直到調整完成。使用文件 rename 防止在動態調整過程中因為宕機導致的文件丟失。
相關參數
參數 | 默認值 | 說明 |
---|---|---|
innodb_redo_log_directory | ./#innodb_redo | 指定 redo log 目錄路徑(可獨立于數據目錄) |
innodb_redo_log_capacity | 104857600 (100MB) | 總 redo log 容量(動態調整,單位:字節) |
innodb_redo_log_files | 32 | 最大 redo log 文件數(自動管理實際使用數) |
從第一個文件開始寫,當redo_log0寫滿了,在寫redo_log1,直到最后一個redo_log也寫滿了,繼續從第一個文件開始寫。其中 write pos
是當前記錄的位置,一邊寫一邊移動;CheckPoint是當前要擦除的位置,也是向后推移的。擦除記錄前會確保記錄已經更新到數據文件中。
RodoLog 幾個重要的參數:
-
Innodb_log_group_home_dir:控制 Redo Log 的存放路徑,如果沒有配置,默認在數據目錄下
如果要修改,需要修改 mysql.cnf文件
先在mysql目錄下創建redolog目錄,記得修改權限
接著在redolog目錄下創建
'#innodb_redo'
目錄,記得修改目錄權限為 750 ,文件權限為 640 ,并且修改權限組為 ‘999:999’docker 中mysql 以999 的uid執行
接著配置Redo Log 的目錄為新創建的目錄
[mysqld] server_id=100 log_bin=/var/lib/mysql-bin/mysql-bin binlog_format=row early-plugin-load=keyring_file.so keyring_file_data=/var/lib/mysql/keyring binlog_encryption=on log-error=/var/log/mysql/mysql-error.log innodb_log_group_home_dir=/var/lib/mysql-redolog
然后重啟
docker run \ --name master \ -e MYSQL_ROOT_PASSWORD=master \ -v /data/mysql/master/log:/var/log/mysql \ -v /data/mysql/master/data:/var/lib/mysql \ -v /data/mysql/master/conf:/etc/mysql/conf.d \ -v /data/mysql/master/binlog:/var/lib/mysql-bin \ -v /data/mysql/master/redolog1:/var/lib/mysql-redolog \ -p 3106:3306 \ -d \ mysql:8.0
啟動后mysql會在新的redolog目錄下創建redofile
-
innodb_log_file_size:控制Redo Log的大小,默認值為 48Mb,但是不能超過 512GB除以 innodb_log_files_in_group 參數配置的值,如果 innodb_log_files_in_group 參數的值為2,那么 innodb_log_file_size 參數的最大值為256G.
如果innodb_redo_file_size 參數設置的太小,那么有可能導致Mysql 的redolog 頻繁切換,頻繁的觸發數據的 CheckPoint,刷新臟頁的次數隨之增加,從而影響IO性能。
如果有一個大的事務把所有的Redo Log 寫滿了還沒有寫完,就會導致日志不能切換,Mysql 有可能就不能正常提供服務了。
如果參數設置的太大,雖然可以提升IO性能,但是當Mysql宕機時,恢復的時間也很長。
-
Innodb_log_files_in_group:控制Redo Log的文件數量
CheckPoint
CheckPoint 是Redo Log 中當前要擦除的位置,Redo Log 在擦除記錄前會確保記錄已經更新到數據文件中。因此,也可以認為CheckPoint就是控制數據頁刷新到磁盤的操作。
如果發生宕機重啟,那么將會從CheckPoint之后開始恢復,之前已經刷新的數據頁不需要恢復。
LSN
Innodb的LSN(Log Sequence Number, 日志序列號)。
LSN用來精確記錄Redo Log的位置信息,大小為 8 字節(mysql 5.6.3 之前是4字節)。
LSN的值是根據Redo Log 寫入量計算的,寫入多少字節的Log, LSN就增長多少。
可以通過 show engine InnoDB status\G
查看
=====================================
2025-08-02 09:10:54 140514356577856 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 2 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 1 srv_active, 0 srv_shutdown, 676 srv_idle
srv_master_thread log flush and writes: 0
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 10
OS WAIT ARRAY INFO: signal count 12
RW-shared spins 0, rounds 0, OS waits 0
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 5902
Purge done for trx's n:o < 5899 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421989504019672, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421989504018864, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421989504018056, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (read thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (write thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,ibuf aio reads:
Pending flushes (fsync) log: 0; buffer pool: 0
874 OS file reads, 275 OS file writes, 108 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
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 34679, node heap has 3 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 29869673
Log buffer assigned up to 29869673
Log buffer completed up to 29869673
Log written up to 29869673
Log flushed up to 29869673
Added dirty pages up to 29869673
Pages flushed up to 29869673
Last checkpoint at 29869673
Log minimum file id is 0
Log maximum file id is 0
32 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 489041
Buffer pool size 8192
Free buffers 7192
Database pages 996
Old database pages 387
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 853, created 143, written 201
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: 996, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=1, Main thread ID=140514029545024 , state=sleeping
Number of rows inserted 0, updated 0, deleted 0, read 0
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
Number of system rows inserted 8, updated 331, deleted 8, read 4791
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
- Log sequence number: 表示整個數據庫最新的LSN值
- Log flushed up to: 表示最新寫入日志文件的事務產生的LSN值
- Last checkpoint at:表示最新的數據頁刷新到磁盤時的LSN值
Mysql 8.0 的 Redo Log 歸檔
在數據備份過程中可能會復制 Redo Log。如果Mysql 的寫入比較多,那么復制 Redo Log 的速度就會跟不上 Redo Log的生成速度。加上Redo Log是以覆蓋方式記錄的,就有可能會丟失部分Redo Log。
Mysql 8.0.17 引入了 Redo Log 歸檔,按照 Redo Log 記錄順序寫入歸檔文件中,以解決備份時Redo Log丟失的問題。
Redo Log 的歸檔由innodb_redo_log_archive_dirs
參數控制。
首先創建一個Redo Log 的歸檔文件夾:
設置參數
因為是docker 啟動的MySQL,因此需要先關閉MySQL,在將 Redo Log 掛載進去
docker run \
--name master \
-e MYSQL_ROOT_PASSWORD=master \
-v /data/mysql/master/log:/var/log/mysql \
-v /data/mysql/master/data:/var/lib/mysql \
-v /data/mysql/master/conf:/etc/mysql/conf.d \
-v /data/mysql/master/binlog:/var/lib/mysql-bin \
-v /data/mysql/master/redolog:/var/lib/mysql-redolog \
-v /data/mysql/master/redolog_archive:/var/lib/mysql-redolog-archive \
-p 3106:3306 \
-d \
mysql:8.0
設置Redo Log 的歸檔目錄
set global innodb_redo_log_archive_dirs='redolog-archiving:/var/lib/mysql-redolog-archive';
redolog-archiving 表示歸檔目錄的標識符,冒號后面的才是歸檔目錄
注意此時僅僅是設置了Redo Log 的歸檔目錄,mysql不會自動開始歸檔
還需要啟動歸檔
do innodb_redo_log_archive_start("redolog-archiving", "redo-0001");
需要注意,如果mysql實例被禁用了Redo Log ,那么啟動歸檔會失敗
Error executing DO statement. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘"’ at line 1 - Connection: master: 350ms
redolog-archiving
是定義的歸檔目錄的標識符,redo-0001
是保存歸檔文件的子目錄。
使用show global status like 'innodb_redo_log_enabled'
查看是否禁用
使用alter instance enable innodb redo_log;
啟用Redo Log
需要注意的是如果需要使用子目錄,比如 redo-0001
,此時需要自己創建
Error executing DO statement. Redo log archive directory ‘/var/lib/mysql-redolog-archive/redo-0001’ does not exist or is not a directory - Connection: master: 311ms
創建了目錄,并且正確的設置了權限后,就會成功啟動
DO statement executed successfully. - Connection: master: 210ms
接著查看歸檔目錄
此時會在Redo Log的歸檔目錄下創建對應的Sql文件
執行一個sql,看看會不會刷新到歸檔目錄的sql文件中
create database test;
use test;
create table t(id int,name varchar(256));
insert into t values(1, 'test');
停止歸檔
do innodb_redo_log_archive_stop();
Mysql 8.0 中的 Redo Log 禁用
從 Mysql 8.0.21開始可以禁用 Redo Log。
可以在數據導入過程中加速。
Mysql 即使禁用 Redo Log,在Mysql 啟動時,依然會檢查 Redo Log 目錄,并且如果Redo Log目錄中存在CheckPoint之后的數據,依然會檢查完整性,并嘗試恢復數據。
只有當Mysql啟動后,才能禁用 Redo Log。禁用后會永久生效。
禁用
alter instance disable innodb redo_log;
查看
SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
啟用
alter instance enable innodb redo_log;
從實際測試情況來看,禁用與啟用 redo log 有 10%~30% 的執行時間差異
https://segmentfault.com/a/1190000023415055
官網文檔
https://dev.mysql.com/doc/refman/8.4/en/innodb-redo-log.html