MySql中的undo.log

前言:关于Mysql底层比较难理解的三种日志,虽然外面面试问的较少,问到的也是一些大厂,但是学习不是为了蒙混面试官的,是为了自己的,所以我相信,熟读事务支持的三种日志,会给我们设计分布式事务或者更加复杂的业务场景带来很大帮助。

(1)是什么?常见场景和用法?

undo.log 也称之为回滚日志,主要用于做事务的数据回滚。更直白的说法,就是做数据回滚恢复的,保证事务的原子性,一个操作失败,全部操作回滚。
undo.log属于逻辑日志,逻辑日志的意思就是它属于一种理论逻辑,就是按道理说当前行在“某些条件下”~‘应该’是怎样的,而不一定物理存储就一定是那样的,与之对应的是redo.log物理日志。再简单点说,逻辑就像产品经理的天荒夜谈,物理就是我们程序员要变成的落地实现。所以回滚日志的性质,就决定了它必然是逻辑日志。
常见的场景就是我们加了事务控制的时候,针对多个不同的事务ID,看到的数据值都是不一样的(不同隔离级别稍有差异),原因就是事务ID不一样,对应的视图副本read view也不一样,视图副本不保存数据,但是会保存新老事务版本ID,通过老的版本ID,就能在undo.log中找到对应的旧数据,这是在MVCC可见性中的应用。
同时三大隐藏列中的回滚指针,也会指向undo.log中的旧数据,方便回滚,这就是最常见的数据回滚中的应用。

(2)undo.log的两种基本格式?
undo.log的基本结构是回滚段,每个回滚段内包含着1024事务的记录.

这两种格式是针对操作方式来说的:
insert插入操作,undo.log中只会记录当前操作值,不存在回滚指针(也就是新增的row无对应的回滚指针),不存在旧值(废话)。
在这里插入图片描述

update,delete 操作,有回滚指针,有旧值。(因为删除操作我们都知道不是真正删除,只是在该列数据加上了deleted_bit标志删除,在MVCC可见性比较的时候,默认不再展示了而已,那个自增ID的位置会一直被霸占着)
在这里插入图片描述

网上有很多说是undo.log里面只是单纯记录了相反的操作,例如insert就delete,update就反向update,delete就insert,这是不对的,它在回滚的时候“整体”“大概”“看上去”的操作虽然是这种逆向操作(因为它确实是做了数据恢复),但是它的记录格式远比一条逆向SQL复杂得多!!要处理的问题也要复杂,例如insert时候生成的索引,delete和update时候的索引重排。
这一点在我们设计分布式事务控制的时候可以参考,在其他微服务调用出现了异常时候可以通过逆向SQL实现手动的数据回滚,当然这是最笨的方法,大佬们有更好的方法一定要提出来分享下。
(3)undo.log实现回滚的时机?

不管是逻辑日志还是物理日志,都是在做了持久化之后才能保证操作的不丢失,在这个从内存中的修改,刷到磁盘中的过程中会有延迟(肯定不是实时刷),如果有些数据操作到一半挂掉了,如何保证事务,所以就需要把内存中的操作刷盘写入到磁盘,那么多久写一次,从哪里作为恢复点会最合适?
Innodb引擎从写操作语句开始:
(1)修改内存中的数据,并在对应数据页记录LSN(日志逻辑序列号,会随着日志的写入增大)
(2)修改完数据的同时会向redo log buffer 写入 redo log,这里会在 log 内记录下LSN
(3)一定间隔内往undo.log塞入CheckPoint检查点,触发check point将内存中的改动的数据统一刷入磁盘,在刷入结束后加check point end。等待下一次刷盘。
(4)刷盘过程会很慢,在刷入过程中,会记录每个已经刷入的LSN。
(5)经过上面的流程,在掉线恢复的时候,会将最新版本号的已经修改了但是没有commit的事务进行回滚,回滚也不可能说全部undo.log走一遍,所以Check Point就是一个分界点。因为它可以保证前面的数据都是已经成功刷盘的了。
(6)从后往前面扫读 undo.log
(7)假如扫到 check point , 证明上一次发生了刷盘,则把check point后的所有事务进行回滚。
(8)假如先扫到 check point end ,证明上一次刷盘成功,只需要把 check point end 后面的所有事务进行回滚。

(4)undo.log在Mysql5.7+的版本中的参数

在这里插入图片描述
innodb_undo_tablespaces:设置undo独立表的空间个数,范围是0-128,默认0表示不开启独立的undo表空间。存储在ibdata文件中。如果设置为2,则表空间文件有undo001,undo002,等等,每个空间大小默认为10M。(建议设置大于等于2或者直接默认),如果设置独立表空间小于2,那么在truncate删节操作中,无表空间可用。
innodb_max_undo_log_size:日志的最大空间,当文件超出它默认的1G后(这里是以B为单位,1273741824B= 1G),将被标记为可收缩。
innodb_undo_directory:单独存放undo表空间的目录,可以设置相对路径或者绝对路径。不可直接修改,要通过停止服务,修改配置文件,再移动表空间文件到对应目录方式修改。(生产环境慎用)
innodb_undo_logs:设置回滚段的个数,5.7之前的Mysql版本这个参数是innodb_rollback_segments。默认是128,其中每个回滚段中又能支持1024个事务。这些回滚段会平均分布到每个表空间。
innodb_undo_log_truncate:删节开关,就是专门清理表空间的。可以看到默认为off,什么时候自动编程on打开状态呢?必须满足两个条件:回滚段的个数>=35(Mysql做的限制,具体原理自己看底层细究),innodb_undo_tablespaces>=2。可以理解为控制回滚段个数的同时根据innodb_max_undo_log_size最大空间来控制存放日志的undo表空间。如果前两个条件不满足,还有一个innodb_purge_rseg_truncate_frequency,它是在Innodb协调线程Purge做了(默认的)128次事务操作后会触发的一个History Purge,检查空间状态是否需要删节缩减。默认被删节后的undo文件表空间大小大约为10M。
在这里插入图片描述

(5)什么是Purge操作?

其实就跟JVM的垃圾回收一样,回收一些老版本的根本没有访问的undo log ,所以innodb 引擎会有个Purge线程专门负责这个工作。
步骤如下:
1:首先获取当前活跃试图read view版本最老的一个,所有在这个最老的活跃视图副本的事务ID版本之前的事务,都是可清理的。由此获得了对应的事务ID范围和回滚指针。
2:根据回滚指针,找到undo.log的对应位置,默认一次性取出300条undo记录。
3:Purge开始多线程清理。包括清理索引文件。
4:清理history list中的对应undo log 回滚段记录。

参考文章:undo.log大佬分享
因为底层日志这一块学习资料非常稀少,视频资源也没有,博客也是五花八门,这也是我自己做的一些总结,希望有大佬看到总结错的地方可以帮忙纠正一下,帮助更多学习的孩子。

猜你喜欢

转载自blog.csdn.net/whiteBearClimb/article/details/109261336
今日推荐