MySQL日志

MySQL日志

1. undo log

1.1 undo log的实现

在事务提交之前,每当InnoDB引擎对一条记录进行增、删、改操作时,要把操作之前的信息记录到undo log中。如果事务还没提交就发生了崩溃,那么就可以通过undo log回滚到之前的数据。

一条记录的每一次更新操作产生的undo log格式都有一个roll pointer指针trx_id事务id

  • 通过trx_id可以知道是那个事务修改的

  • 通过roll pointer可以将这些记录串成一条链,称为版本链:

    版本链

1.2 undo log的作用

  1. undo log可以和ReadView配合,实现MVCC,实现了事务的隔离性C
  2. 实现事务的回滚,保障事务原子性A

2. Buffer Pool

如果每次取数据、写数据都直接对硬盘进行操作,那么时间开销会很大。最好的方法当然是先写到一个缓存中,后面再由缓存慢慢写到磁盘中去,这个缓存就是Buffer Pool

Buffer Pool将数据划分为若干个 页 ,以页作为磁盘和内存的交互单位,一个页默认大小为16KB。当某一页中有数据发生了更改,那么这一页就会被标记为脏页,后续会将脏页回写到磁盘中。

由于undo log也要回写到磁盘中去,所以Buffer Pool中也有对应的Undo页,undo log发生修改时,先写到Buffer Pool中的Undo页中,后续再回写到磁盘中。

3. redo log

Buffer Pool确实提升了读写效率,但是如果机器断电重启,此时内存中尚未写回磁盘中的脏页就丢失了,redo log就可以用来解决这个问题。

当有一条记录需要更新时,先更新BufferPool中对应的页,并标记为脏页,然后将本次对这个页的修改以redo log的形式保留下来,这时候更新就算是完成了。后续会在适当时候,由后台线程将缓存在BufferPool中的脏页刷新到磁盘去。这就是WAL(Write-Ahead Logging)技术

也就是说,redo log记录的是:仍在BufferPool,尚未被刷入磁盘中的脏数据。(区别于undo log:记录事务提交前的数据)所以undo log对应的Undo页面也会记录到redo log中。

3.1 redo log要写入到磁盘,数据也要写入磁盘,为什么需要多此一举

因为redo log属于是顺序写,而数据写入磁盘是随机写,相比之下肯定是顺序写的效率更高。

所以redo log的作用:

  1. MySQL崩溃重启后已提交的记录不丢失(Buffer Pool中尚未刷入磁盘的脏页),实现事务持久性D
  2. 将数据的随机写变为日志的顺序写,提升了效率

redo log文件采用循环写的操作,分成两个文件ib_logfile0和ib_logfile1:

  • write pos ~ checkpoint之间的部分:空闲的,用来记录新的更新操作
  • checkpoint ~ write pos之间的部分:带落盘的脏页数据
img

如果write pos追上了checkpoint,就意味redo log文件满了,此时MySQL不能再执行新的更新操作,会被阻塞。会停下来将脏页数据刷入磁盘直到redo log有空闲空间为止(对并发大的系统,redo log的大小很重要)

4. bin log

相较于redo log记录的是脏页数据,bin log记录的则是全量数据(默认记录的是每一条修改数据的SQL)

binlog有3种格式类型,分别是STATEMENT(默认格式)、ROW、MIXED,区别如下:

  • STATEMENT:每一条修改数据的SQL都会被记录到binlog中(相当于记录了逻辑操作,所以针对这种格式,binlog可以称为逻辑日志),主从复制中slave端再根据SQL语句重现。但STATEMENT有动态函数的问题,比如你用了uuid或者now这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
  • ROW:记录行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现STATEMENT下动态函数的问题。但ROW的缺点是每行数据的变化结果都会被记录,比如执行批量update语句,更新多少行数据就会产生多少条记录,使binlog文件过大,而在STATEMENT格式下只会记录一个update语句而已;
  • MIXED:包含了STATEMENT和ROW模式,它会根据不同的情况自动使用ROW模式和STATEMENT模式;

4.1 bin log和redo log的区别

  1. binlog属于Server层,redolog属于Innodb引擎
  2. 文件格式不同
  3. 写入方式:binlog属于追加写,redolog属于循环写
  4. 用途:binlog用于主从复制、数据库被删除时恢复,redolog用于故障恢复

4.2 bin log主从复制

image-20251211190300250

通过返回响应的区别,MySQL主从复制分为了如下三种模型:

  1. 同步复制:主库要等所有从库成功响应
  2. 异步复制:主库不等
  3. 半同步复制:只要有一个从库成功响应就行

5. 两阶段提交

事务提交后,redo log 和 binlog 都要持久化到磁盘,但是这两个是独立的逻辑,可能出现半成功的状态,这样就造成两份日志之间的逻辑不一致

举个例子,假设 id = 1 这行数据的字段 name 的值原本是 ‘jay’,然后执行 UPDATE t_user SET name = ‘xiaolin’ WHERE id = 1;如果在持久化 redo log 和 binlog 两个日志的过程中,出现了半成功状态,那么就有两种情况:

  • 如果在将 redo log 刷入到磁盘之后,MySQL 突然宕机了,而 binlog 还没有来得及写入。MySQL 重启后,通过 redo log 能将 Buffer Pool 中 id = 1 这行数据的 name 字段恢复到新值 xiaolin,但是 binlog 里面没有记录这条更新语句,在主从架构中,binlog 会被复制到从库,由于 binlog 丢失了这条更新语句,从库的这一行 name 字段是旧值 jay,与主库的值不一致性;
  • 如果在将 binlog 刷入到磁盘之后,MySQL 突然宕机了,而 redo log 还没有来得及写入。由于 redo log 还没写,崩溃恢复以后这个事务无效,所以 id = 1 这行数据的 name 字段还是旧值 jay,而 binlog 里面记录了这条更新语句,在主从架构中,binlog 会被复制到从库,从库执行了这条更新语句,那么这一行 name 字段是新值 xiaolin,与主库的值不一致性;

所以两阶段提交就是为了解决redo log和bin log不一致的问题。

5.1 两阶段提交流程

两阶段提交

从图中可看出,事务的提交过程有两个阶段,就是将redo log的写入拆成了两个步骤:prepare和commit,中间再穿插写入binlog,具体如下:

  • prepare阶段:将XID(内部XA事务的ID)写入到redo log,同时将redo log对应的事务状态设置为prepare,然后将redo log持久化到磁盘(innodb_flush_log_at_trx_commit = 1的作用);
  • commit阶段:把XID写入到binlog,然后将binlog持久化到磁盘(sync_binlog = 1的作用),接着调用引擎的提交事务接口,将redo log状态设置为commit,此时该状态(commit状态)并不需要持久化到磁盘,只需要write到文件系统的page cache中就够了,因为只要binlog写磁盘成功,就算redo log的状态还是prepare也没有关系,一样会被认为事务已经执行成功;

5.2 两阶段提交如何解决异常

时刻 A 与时刻 B

不管是时刻 A(redo log 已经写入磁盘,binlog 还没写入磁盘),还是时刻 B(redo log 和 binlog 都已经写入磁盘,还没写入 commit 标识)崩溃,此时的 redo log 都处于 prepare 状态

在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的 XID 去 binlog 查看是否存在此 XID:

  • 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。
  • 如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务。对应时刻 B 崩溃恢复的情况。

可以看到,对于处于 prepare 阶段的 redo log,即可以提交事务,也可以回滚事务,这取决于是否能在 binlog 中查找到与 redo log 相同的 XID,如果有就提交事务,如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。

所以说,两阶段提交是以 binlog 写成功为事务提交成功的标识,因为 binlog 写成功了,就意味着能在 binlog 中查找到与 redo log 相同的 XID。


MySQL日志
http://example.com/2025/12/11/MySQL日志/
作者
Kon4tsu
发布于
2025年12月11日
许可协议