Undo log
悔做日志(Undo Log)是数据库事务管理的一个关键组成部分,尤其是在支持事务的存储引擎(如 MySQL 的 InnoDB 存储引擎)中。Undo Log 主要用于记录事务对数据的修改操作的反向操作,以便在事务回滚时撤销这些修改。在 InnoDB 存储引擎中,Undo Log 用于实现事务的原子性(Atomicity)和隔离性(Isolation)。
Undo Log 的主要功能如下:
以下是 InnoDB 存储引擎中 Undo Log 的工作流程:
总之,Undo Log 是数据库事务管理的一个重要组成部分。在 InnoDB 存储引擎中,Undo Log 起到了撤销事务更改和实现事务隔离性的作用。
Binary Log
BinLog数据模式
- 查看BinLog日志模式
select @@binlog_format
Row Level 行模式
Row Level 模式会记录每一行数据被修改的形式,在slave端再对相同的数据进行修改。这种模式日志记录最为完整,但是日志量大。日志格式如下:
借助mysqlbinlog工具,解析上述日志,得到一下结果:
- row 格式下,insert 语句的 binlog 里会记录所有的字段信息,这些信息可以用来精确定位刚刚被插入的那一行。这时,你直接把 insert 语句转成 delete 语句,删除掉这被误插入的一行数据就可以了。
- 如果执行的是 update 语句的话,binlog 里面会记录修改前整行的数据和修改后的整行数据。所以,如果你误执行了 update 语句的话,只需要把这个 event 前后的两行信息对调一下,再去数据库里面执行,就能恢复这个更新操作了。
Statement Level(默认)
Statement Level 记录每一条修改数据的sql,slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行。这种模式能够减少减少bin-log日志量,但是在修改数据的时候使用了某些定的函数或者功能的时候会出现问题(主备不一致)。日志格式如下:
这种格式的日志是存在风险的,比如以上诉delete语句为例,因为带了limit,可能会导致主备不一致,比如,在主库上delete语句选择了索引a,删除的是索引a找到的第一个满足条件的行,如果在备库上选择的是索引b,那么删除的是索引b找到第一个满足条件的行。
Mixed 自动模式
Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志格式,也就是在Statement和Row之间选择一种。MySQL 自己会判断这条 SQL 语句是否可能引起主备不一致,如果有可能,就用 row 格式,否则就用 statement 格式(一般如果sql语句是update或者delete等修改数据的语句,那么还是会记录所有行的变更。)
BinLog写入机制
当一个事务执行完成并提交时,MySQL 会将事务中的更改写入 binlog。在事务提交之前,这些更改被缓存在 binlog 缓存中。当事务提交时,MySQL 会将缓存中的 binlog 事件刷新到磁盘。
- 每个线程有自己 binlog cache,但是共用同一份 binlog 文件。
- write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。
- fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为 fsync 才占磁盘的 IOPS。
- write 和 fsync 的时机,是由参数 sync_binlog 控制。
- sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync。
- sync_binlog=1 的时候,表示每次提交事务都会执行 fsync。
- sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
- 将 sync_binlog 设置为 N,对应的风险是:如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。
Redo log
在 MySQL 中,当使用 InnoDB 作为存储引擎时,重做日志(redo log)是一种非常重要的日志。redo log 主要用于确保事务的持久性(Durability),并在数据库崩溃时提供故障恢复能力。
- InnoDB 引擎特有,记录的是“在某个数据页上做了什么修改”
- redo log 是循环写的,空间固定会用完
- Innodb 引擎使用 WAL(Write-Ahead Logging)技术来提升效率,技术的关键是先写日志再写磁盘。当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面。
- crash-safe:有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
- 检查点(Checkpoints):为了避免在数据库崩溃时回放过多的 redo log 数据,InnoDB 使用检查点(checkpoints)机制。检查点是 InnoDB 数据文件和 redo log 文件之间的同步点。InnoDB 会定期将 redo log 中的数据刷新到数据文件中,并将检查点信息更新到磁盘。在数据库崩溃时,只需要回放检查点之后的 redo log 数据即可。检查点有助于减少故障恢复时间。
- 为什么有 binlog 还要 redo log:binlog 不具备奔溃恢复的能力,这是因为 binlog 没有 check point,无法确定从哪里开始重放日志。
Redo Log日志结构
redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么日志总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下图所示。
图中展示了一组 4 个文件的 redo log 日志,checkpoint 是当前要擦除的位置,擦除记录前需要先把对应的数据落盘(更新内存页,等待刷脏页)。write pos 到 checkpoint 之间的部分可以用来记录新的操作,如果 write pos 和 checkpoint 相遇,说明 redolog 已满,这个时候数据库停止进行数据库更新语句的执行,转而进行 redo log 日志同步到磁盘中。checkpoint 到 write pos 之间的部分等待落盘(先更新内存页,然后等待刷脏页)。
Redo log 写入机制
redo log有三种状态
- 存在 redo log buffer 中,物理上是在 MySQL 进程内存中,就是图中的红色部分;
- 写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里面,也就是图中的黄色部分;
- 持久化到磁盘,对应的是 hard disk,也就是图中的绿色部分。
InnoDB 提供了 innodb_flush_log_at_trx_commit 参数用于控制 redo log 的写入策略
- 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中
- 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘
- 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache
InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。
两段提交与奔溃恢复
假设我们要更新ID=2的行,执行过程如下:
在 MySQL 中,当使用 InnoDB 存储引擎并启用二进制日志(binlog)时,会使用两段提交协议来确保数据的一致性。在这种情况下,两段提交协议涉及到重做日志(redo log)和二进制日志(binlog)的同步。
两段提交协议在 MySQL InnoDB 存储引擎中的运作如下:
通过两段提交协议,MySQL 可以确保在事务提交过程中,redo log 和 binlog 的同步。在数据库崩溃时,可以通过回放 redo log 和 binlog 来恢复数据的一致性。如果在第一阶段失败,事务会被回滚;如果在第二阶段失败,事务已经被记录在 redo log 中,因此可以在数据库恢复后继续完成提交。
假设不使用两段提交,会有一下情况:
在恢复数据时,MySQL 会使用事务 ID(Transaction ID,简称 XID)来跟踪和确定事务的状态。这里所谓的xid,是这样的:
以下是 MySQL 系统崩溃时,通过 redo log 和 binlog 恢复数据的过程:
- 重做日志恢复(Redo Log Recovery):当数据库启动时,InnoDB 存储引擎会先进行 redo log 的恢复。这一步骤会检查 redo log 中的所有记录,找到最近的检查点(Checkpoint),从检查点开始,将所有 PREPARED 或 COMMITTED 状态的事务重做(即应用到数据文件)。这样可以确保已提交事务的更改被正确应用到数据文件。在这个过程中,XID 用于确定事务的状态,以便决定是否需要重做。
- 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
- 如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
- 悔做日志恢复(Undo Log Recovery):在 redo log 恢复完成后,InnoDB 会进行悔做日志(undo log)恢复。悔做日志用于存储事务的回滚信息。悔做日志恢复的目的是回滚那些在数据库崩溃时尚未提交的事务。InnoDB 会根据悔做日志中的 XID 回滚未提交的事务,以确保数据的一致性。
- 二进制日志恢复(Binlog Recovery):在 redo log 和 undo log 恢复完成后,如果启用了二进制日志(binlog),则会进行 binlog 恢复。在这个阶段,MySQL 会从最后一个完整的 binlog 备份开始,逐条执行 binlog 中的事件,将所有已提交事务的更改应用到数据文件。这样可以确保那些在 redo log 恢复阶段尚未被应用的事务得到正确的恢复。在这个过程中,XID 用于标识事务的开始和结束,以确保事务的原子性。
Relay log(中继日志)
Relay log(中继日志)是 MySQL 主从复制过程中的一个关键组件。在主从复制配置中,主服务器(Master)会将其二进制日志(Binary Log,简称 binlog)中的更改发送给从服务器(Slave)。从服务器接收到这些更改后,会将它们写入到本地的 Relay log。然后,从服务器会根据 Relay log 中的记录应用这些更改到自己的数据文件。这样,从服务器的数据可以保持与主服务器的数据同步。
Relay log 的作用主要有以下几点:
Relay log 的工作流程如下: