Undo log 回滚日志是 MySQL 进阶必知必会的一个重要知识点,大厂面试也经常会被问到。
Undo Log 回滚日志记录事务执行过程中的旧值,当事务发生异常、需要回滚操作时,可使用 Undo Log 来撤销事务的变更操作,将数据恢复到事务开始之前的状态,从而确保事务的一致性,提高数据库的并发性能和系统的稳定性。
大家好,我是爱分享的程序员宝妹儿。
本篇深入:Undo log 回滚日志的物理结构、以及其在事务中的流程。
PS.
刚结束的 Binlog 系列,宝子们的反馈还不错,例如:例如:binlog从基础到精通系列,图解bin log、redo log及undo log区别等,宝妹儿已将内容更新到《MySQL 大厂高频面试题大全》PDF了,方便系统学习、面试通关。
《MySQL 大厂高频面试题大全》PDF,已收录100+道真题,一共78页,近30000字,文末自取。
吃透它,足以应付MySQL面试。
1. Undo log 基础知识
上一篇 图解 Undo log 的存储机制及工作原理 ,介绍了:
- Undo log 的概念
- Undo log 的两大作用
- Undo log 的存储机制
- Undo log 的工作原理
点击蓝字回顾: 图解 Undo log 的存储机制及工作原理
2. Undo log 的作用
Undo log 的两大作用分别是提供数据回滚、多版本控制 MVCC。
- 提供数据回滚(原子性)
事务具有 ACID 特性,其中,”A”代表事务的原子性(Atomicity)。
即:事务中的所有操作要么全部成功执行,要么全部回滚。
undo log(回滚日志)的回滚操作是实现原子性的关键,它保证了事务的 ACID 特性中的原子性。
undo log 是一种用于撤销回退的日志,在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时或数据库崩溃时,可以利用 Undo Log 进行数据回滚。
- 多个行版本控制 MVCC
undo log 还有一个作用,通过 ReadView + undo log 实现 MVCC。
当用户读取某个记录时,如果这个记录已被其它的事务所占用,通过 Undo Log 读取之前的行版本信息,当前事务就可以实现非锁定读取。
undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。
一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
- 通过 trx_id 可以知道该记录是被哪个事务修改的;
- 通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;
版本链如下图:
3. Undo log 的物理结构
Undo log 的物理结构
Undo log 的物理结构,由上到下分别为:
- 回滚表空间
- 回滚段 (undo log segment)
- undo log slot
- page
Undo log 由 128 个回滚段(Rollback segment)构成。
其中,32 个回滚段用于系统的临时表空间,96 个回滚段用于事务。
- slot 0 ,预留给系统表空间;
- slot 1- 32,预留给临时表空间,每次数据库重启的时候,都会重建临时表空间;
- slot 33-127,如果有独立表空间,则预留给UNDO独立表空间;如果没有,则预留给系统表空间。
回滚段中除去 32 个提供给临时表事务使用,剩下的 128-32=96个回滚段,可执行 96*1024 个并发事务操作,每个事务占用一个 undo segment slot。
- 如果事务中有临时表事务,在临时表空间中的 undo segment slot 会再占用一个 undo segment slot,即占用2个 undo segment slot。
- 如果错误日志中有 Cannot find a free slot for an undo log,说明并发的事务太多了,需要考虑是否分流业务。
每个回滚段对应一个 segment header page,在这个 page 中又划分了 1024 个 slot,每个 slot 又对应到一个 undo log 对象。
独立表空间的 space id 是从 1 开始的,0 被预留在 ibdata 中,space id 是连续分配、并且不能断档的。
回滚段
重点聊下回滚段。
回滚段(Rollback Segment)由多个回滚段组成,是存储 Undo log 的数据结构,每个回滚段有多个 undo log slot,负责存储特定范围的事务 Undo log 。
回滚段采用轮询调度的方式来分配使用,如果设置了独立表空间,就不会使用系统表空间回滚段中 undo segment,而是使用独立表空间,同时,如果回滚段正在 Truncate 操作,则不分配。
InnoDB 在 undo tablespace 中使用回滚段来组织 undo log,以及维护 undo log 的并发写入和持久化。
通过 Rollback Segment Header 来管理回滚段,Rollback Segment Header 的结构:
回滚段有 update_undo、insert_undo 两种类型。
update_undo:
- 只用于事务内的 update 和 delete 语句。
- 加入到其对应 rollback segment 的 history list 数据页列表上,history list 长度加1。
- 该 log 需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表,等待 purge 线程进行清除。
insert_undo:
- 只用于事务内的 insert 语句。
- 新插入的记录产生的 undo 不会被任何查询语句所引用,因此可以直接释放 undo,此处 undo log 不会累加到 history list 上。insert 操作的记录只对事务本身可见,因此,该 undo log 不需要进行 purge 操作,在事务提交后就直接删除。
delete 和 update 操作最终都是由 purge 来完成的。
- innodb_purge_batch_size:全局动态参数,默认值为 300,用来设置每次 purge 操作需要清理的 undo page 数量。
- innodb_max_purge_lag:全局动态参数,用来控制 history list 的长度,若大于该参数时,其会延缓 DML 的操作。
- innodb_max_purge_lag_delay:全局动态参数,用来控制 DML 操作每行数据的最大延缓时间,单位为毫秒。
可以通过如下几个参数对回滚段做配置:
innodb_undo_log_truncate
- InnoDB 的 purge 线程,根据 innodb_undo_log_truncate 设置开启或关闭、innodb_max_undo_log_size 的参数值,以及 truncate 的频率来进行空间回收和 undo file 的重新初始化。
- 该参数生效的前提是,已设置独立表空间且独立表空间个数大于等于 2 个。
innodb_max_undo_log_size
- 控制最大的 undo tablespace 文件大小,当启动了 innodb_undo_log_truncate 、且 undo tablespace 超过 innodb_max_undo_log_size 阀值时,才会去尝试 truncate 。
- 该值默认大小为 1G,truncate 后的大小默认为 10M。
innodb_undo_tablespaces
- 设置 undo 独立表空间个数,范围为 0-128, 默认为 0,0 表示不开启独立 undo 表空间、且 undo日志存储在 ibdata 文件中。
- 该参数只能在最开始初始化 MySQL 实例的时候指定,如果实例已创建,这个参数是不能变动的,如果在数据库配置文件 .cnf 中指定 innodb_undo_tablespaces 的个数大于实例创建时的指定个数,就会启动失败,并提示该参数设置错误。
innodb_undo_directory
- 设置 rollback segment 文件所在位置的路径,这意味着 rollback segment 可以存放在共享表空间以外的位置,即可以设置为独立表空间。
- 该参数的默认值为“.”,标识当前InnoDB存储引擎的位置的目录。
innodb_undo_log
- 用来设置 rollback segment 的个数。
- 默认值为128个。
4. Undo log 在事务中的流程
事务在 undo log segment 分配、且写入 undo log 的同时,也会产生 redo log。
当事务提交时(commit):
- 将 undo log 放入列表中,以供之后的 purge(pai rui chi) 线程使用;
- 判断 undo log 所在的页能否重用,如果可以重用,则分配给下个事务使用;
- 事务提交后,不能马上删除 undo log 及 undo log 所在的页,这是因为还有其他事务需要通过 undo log 来得到行记录之前的版本,故事务提交将 undo log 放入一个链表中,是否可以最终删除 undo log 及 undo log 所在页由 purge 线程来判断。
当事务提交时,先将 undo log 放入链表中,判断 undo 页的使用空间是否小于 3/4(表示 undo log 可否被重用)。
之后新的undo log 记录在当前的 undo log 的后面,由于存放 undo log 的列表是以记录进行组织的,undo log 可能存放着不同事务的 undo log。
因此,prege 操作需要设计磁盘的离散读取操作,这个过程很缓慢,可以通过设置 purge 线程的个数来提升回收速度。
总结
在 MySQL 中 ,Undo log 回滚日志是数据库事务的重要组成部分,在事务执行变更操作之前先将相反的操作写入 Undo log ,当事务回滚、或数据库崩溃时,通过记录事务执行过程中的旧值,实现对数据库修改的撤销回退,从而确保数据的一致性和可靠性,Undo log 也是实现多版本控制( MVCC )的基础。
通过本文,我们掌握了 Undo log 回滚日志的概念、原理以及在事务中的流程等,可以帮助大家进一步理解、正确应用 Undo log 。
建议收藏备用,划走就找不到啦。
如果有用,请顺手给宝妹儿【关注+点赞】下哦,拜谢。
PS.
本文已收录于宝妹儿精编的 2023版《MySQL 大厂高频面试题大全》PDF。
《MySQL 大厂高频面试题大全》一共78页,34000字,图文并茂,长期持续更新。
吃透它,足以应对 MySQL 面试。