InnoDB redo
一、REDO简介
- redo log用于记录除了 SELECT的所有操作。
- 重做日志作用是:当崩溃恢复期间用于前滚已经提交的数据,回滚未提交的数据。
- MySQL以循环方式写入重做日志文件。
- 重做日志的增加以LSN值表示。
- 切换redo log的时候发生checkpoint,触发脏页的刷新
- 默认情况下,innodb会创建2组大小均为5M的REDOLOG,分别为ib_logfile0、ib_logfile1,保存在datadir指定的路径下。相关的参数有下列几个
- innodb_log_group_home_dir:指定redolog保存路径,默认在datadir指定的路径下
- innodb_log_file_size:指定日志文件的大小,默认是5M,最大512G.该参数会影响检查点的执行频率,以及故障的恢复时间。一般来说,日志设置的越大,检查点执行的频率就越低,但是如果在这期间发生故障,启动的时候就会越长。
- innodb_log_files_in_group:指定日志文件组的数量
- innodb_fast_shutdown
- 0:等到会话结束,事务结束,缓冲区的数据刷新到磁盘,类似shutdown normal
- 1:关闭会话终止连接,将已提交的刷新到数据文件,未提交的回滚。类似shutdown immediate
- 2:忽略所有操作,直接关闭数据,类似shutdown abort
以上参数不支持动态修改
- 触发 log buffer 刷新到 redo log的条件
- commit,innodb_flush_log_at_trx_commit参数控制
- 0:每秒刷新
- 1:commit 的时候刷新到磁盘
- 2:commit时刷新到系统的buffer中
- redo log thread:1s,可以通过 innodb_flush_log_at_timeout 参数控制
- redo log buffer:1/2
二、redo log 跟 binlog
2.1 redo log 跟 binlog的区别
第一:记录的内容不同
binlog记录数据的改变信息
redo log记录的是页的改变信息
第二:记录的时间不同
binlog记录commit后的操作
redolog记录事务发起后的操作
第三:文件使用方式不同
binlog写完会生成新的文件
redolog最后一个文件写完,会覆盖第一个文件
第四:作用不同
binlog可以作为恢复数据,主从搭建
redolog作为异常宕机后 Innodb实例会崩溃恢复
2.2 redo log 跟 binlog的关系
MySQL两阶段提交过程:
- 准备过程:InnoDB 层写 prepare log
- 写的还是 redo file
- 写入的是 xid (事物 id)
- 提交过程:分为2个步骤
- 将binlog cache 中的事务写入 binlog 文件
- 再在redo log中做一个事务提交的标记,并把 binlog 写成功的标记页一并写到 redo log文件中
场景一
准备阶段,redo log刷新到磁盘,但是binlog写盘前发生了MySQL实例crash。这时,即使redo log写盘成功了,但由于binlog未写入成功,也是需要回滚
场景二
提交阶段,binlog写盘成功了,这时候MySQL实例crash。这是binlog已经确保写成功了,重启MySQL的时候,检测到redo中和binlog中都有xid。那么,只需要让redo 强制提交就可以了
三、组提交
组提交主要是为了解决写日志时频繁刷磁盘的问题。
5.5 时只支持 redo 组提交,5.6之后支持 binlog 组提交,5.6之后的大并发性能问题比5.5好的一个原因
3.1 redo 组提交
事务提交时会进行两个阶段的操作:
- 将日志写入 redo log buffer
- 从 redo log buffer 写入磁盘
在没有 redo 组提交之前,步骤 2 相对于 步骤 1 的速度是较慢的。
出现 redo 组提交之后,多个事物 redo log 刷盘动作合并,减少 IO 调用。它的原理是:
redo log 中的 LSN 是单调递增的,每个事务的产生,都会获取到最大的 LSN。假设事务 trx1,trx2,trx3 对应的 LSN 分别是 LSN1,LSN2,LSN3,如果 trx3 先获取到 log_mutex 进行落盘,他就顺便可以把 LSN1,LSN2,LSN3 这段日志也进行刷盘。
但是,redo 组提交也只限于没有开启 binlog 的情况下,当开启了 binlog 之后,为了保证 redo 和 binlog的写入一致性。就涉及到一个两阶段提交:
prepare 阶段,innodb刷新 redo log,并将 UNDO 设置为 prepare 状态
commit 阶段,binlog 刷盘,最后设置 commit 状态。
对于多个事务的组提交,一个关键是保证事务在redo log和binlog中的顺序一致。上述的2PC并不能保证这一点。所以在 5.5 的时候就引入了 prepare_commit_mutex,见下图
prepare 阶段,持有 prepare_commit_mutex, innodb刷新 redo log,并将 UNDO 设置为 prepare 状态
commit 阶段,binlog 刷盘,最后设置 commit 状态。释放 prepare_commit_mutex。
持有 prepare_commit_mutex 期间,其他事务不能写 redo。这样就保证了串行执行。同时组提交失效,在 5.6 之后,为了解决这个问题,引入了 binlog group commit
3.2 binlog 组提交
binlog 组提交 引入了队列机制保证 innodb commit 顺序与 binlog 落盘顺序一致,并将事务分组,组内的 binlog 刷盘动作交给一个事务进行。binlog 组提交将提交分为了3个阶段
- FLUSH 阶段
- 持有 lock_log mutex (leader 持有,followr 等待)
- 获取队列中的一组 binlog (队列中的所有事务)
- 将 binlog buffer 到 I/O cache
- 通知 dump 线程 dump binlog
- SYNC 阶段
- 释放lock_log mutex,持有 lock_sync mutex(leader 持有,follower 等待)
- 将一组 binlog落盘(sync 动作,假设 sync_binlog 为1)
- COMMIT阶段
- 释放 lock_sync mutex,持有 lock_commit mutiex(leader持有,follower等待)
- 遍历队列中的事务,逐一进行 innodb commit
- 释放 lock_commit mutex
- 唤醒队列中等待的线程
3.3 组提交相关参数
binlog_group_commit_sync_no_delay_count:等待一组里面有多少事物我才提交
binlog_group_commit_sync_delay:等待多少时间后才进行组提交
参考书籍:《MySQL技术内幕》、《MySQL王者晋级之路》
参考博客:https://www.cnblogs.com/cchust/p/4439107.html