MySQL-redo log
参考文献
- 千金良方: MySQL性能优化金字塔法则
redo log
-
redo log
用来实现事务的持久性,即事务ACID
的D
.其由两部分组成:- 一是内存中的重做日志缓冲(
redo log buffer
),其是易失的 - 二是重做日志文件(
redo log file
),其是持久的.
- 一是内存中的重做日志缓冲(
-
InnoDB
是事务的存储引擎,其通过Force Log at Commit
机制实现事务的持久性,即当事务提交(COMMIT
)时,必须先将事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT
操作完成才算完成. -
redo log
基本上都是顺序写的,在数据库运行时不需要对redo log
的文件进行读取操作,而undo log
是需要进行随机读写的. -
WAL(Write-Ahead Logging)
,它的关键点就是先写日志,再写磁盘.- 当有一条记录需要更新的时候,
InnoDB
引擎就会先把记录写到redo log里面,并更新内存,这个时候更新算完成了.同时,InnoDB
引擎会在合适的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做.
- 当有一条记录需要更新的时候,
-
InnoDB
的redo log
是固定大小的,比如可以配置一组4个文件,每个文件的大小为1G,那这个日志总共可以记录4GB的操作.从头开始写,写到末尾就又回到开头循环写.write pos
是当前记录的位置,一边写一边后移,写到3号文件末尾就回到0号文件开头.checkpoint
是当前要擦除的位置,也就是往后推移并且循环的,擦除记录要把记录更新到数据文件.write pos
和checkpoint
之间的还有空着的部分,可以用用来记录新的操作.- 如果
write pos
追上checkpoint
,表示redo log
满了,这时不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint
推进一下.
-
有了
redo log
,InnoDB
就可以保证即使数据库发送异常重启,之前提交的记录都不会丢失,这个能力称为**crash-safe
**. -
redo log
用于保证crash-safe
能力.innodb_flush_log_at_trx_commit
这个参数设置成1的时候,表示每次事务的redo log
都持久化到磁盘.这个参数建议设置成1,这样可以保证MySQL异常重启之后数据不丢失. -
redo log
不是记录数据页"更新之后的状态",而是记录这个页"做了什么改动". -
redo log
文件: 文件名为ib_logfile+数字
- 在 MySQL 8.0 中,重做日志文件的名称通常是
ib_logfile0, ib_logfile1
等, 但默认情况下,它们被拆分成更小的文件,存储在名为#innodb_redo
的目录中
- 在 MySQL 8.0 中,重做日志文件的名称通常是
redo log
作用
redo log
称为重做日志,用来保证事务的原子性和持久性.- 快速提交
- 恢复实例
- 增量备份,以及恢复到某一时间点
- 复制
redo log
存储内容
- 修改的物理数据页的编号以及相对于该页的字节偏移量.
- 对数据页所做的修改操作,如插入、更新或删除操作.
- 事务ID,即修改操作所属的事务编号.
- 物理页修改前的状态,即修改前的数据,为了支持事务的回滚操作.
- Checkpoint,用于记录哪些 redo log 已被持久化,以及哪些还没有被持久化,当数据库重启时可以根据 Checkpoint 来确定从哪里开始进行恢复.
redo log
的写入方式
-
MySQL
每执行一条DML
(redo log
的写入在事务开始后就会一直进行),会先把记录写入日志缓冲区redo log buffer
,缓冲区的大小由
innodb_log_buffer_size
参数控制,后续某个时间点再一次性将多个操作记录写到redo log file
.- 虽然事务没有提交,但相关的操作日志是有可能会被刷新到磁盘上的。事实上确实存在这种情况,解决的办法是:在提交事务时在
redo log
文件中添加一个commit
标记表示对应的记录已经提交,这样即可实现快速提交。
- 虽然事务没有提交,但相关的操作日志是有可能会被刷新到磁盘上的。事实上确实存在这种情况,解决的办法是:在提交事务时在
-
在计算机操作系统中,用户空间(
user space
)下的缓冲区数据,一般是无法直接写入磁盘的,必须经过操作系统内核空间缓冲区(即OS Buffer
).- 日志最开始会写入位于存储引擎
InnoDB
的redo log buffer
,这个是在用户空间完成的. - 然后再将日志保存到操作系统内核空间的缓冲区(OS buffer)中.
- 最后,通过系统调用
fsync()
,从OS buffer写入到磁盘上的redo log file中,完成写入操作.这个写入磁盘的操作,就叫做刷盘.
- 日志最开始会写入位于存储引擎
-
MySQL提供参数
innodb_flush_log_at_trx_commit
用来控制重做日志刷新到磁盘的策略.参数值 作用 0 称为延迟写,事务提交时不会将 redo log buffer
中日志写入到OS buffer
,而是每秒写入OS buffer
并调用写入到redo log file
中.1 称为实时写,实时刷,事务每次提交都会将 redo log buffer
中的日志写入OS buffer
并保存到redo log file
中.2 称为实时写,延迟刷.每次事务提交写入到 OS buffer
,然后是每秒将日志写入到redo log file
.1
2
3
4
5
6
7mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.01 sec)
log block
- 在
InnoDB
存储引擎中,重做日志都是以512字节进行存储的.这意味着重做日志缓冲,重做日志文件都是以块(block
)的方式进行保存的,称之为重做日志块(redo log block
),每块的大小为512字节.
redo log
和binlog
的区别
- 记录内容不同.redo log主要记录了MySQL引擎发生的所有修改操作,用于将数据从缓存中刷新到磁盘中以保证数据的一致性;而binlog主要记录了数据库的所有写操作,如INSERT、UPDATE和DELETE等,主要用于数据恢复、数据同步和数据备份等操作.
- 存储位置不同.redo log存储在
InnoDB
存储引擎的共享表空间中;binlog则存储在MySQL服务器的主机上,可以存储在不同的位置并且有多种存储格式可供选择. - 作用不同.redo log主要用于恢复数据,防止因MySQL崩溃或其他原因导致的数据丢失而无法恢复,因此是MySQL引擎内部的机制;binlog则主要用于将MySQL主数据库中的写操作同步到从数据库中,以实现数据同步和备份等功能,因此也是实现主从复制的关键.
- 记录粒度不同.redo log记录的是每个数据页面的修改,粒度比较小;binlog则记录的是每个事务的执行语句,粒度较大.
redo log
是循环写的,空间固定会用完;binlog
是可以追加写入."追加写"是指binlog
文件写到一定大小后会切换到下一个,并不会覆盖以前的日志.
如何监控redo log
的使用情况呢?
1 | mysql > show engine innodb status\G |
1 | mysql> select name,count from information_schema.innodb_metrics where name in ('log_lsn_current','log_lsn_last_checkpoint'); |
1 | mysql> select * from sys.metrics where variable_name in ('log_lsn_current','log_lsn_last_checkpoint'); |
redo log
的使用量
1 | use log = log_lsn_current - log_lsn_last_checkpoint (单位bytes) |