大家好,我是热心的大肚皮,皮哥。
undo日志的前世
之前聊过,事务需要保证原子性,要么全部完成,要么什么也不做,但是经常会出现事务在执行到一半时会出现情况,例如:
事务执行到一半就结束,但是事务在执行过程中可能修改了很多东西。为了保证原子性,需要改回原来的样子,这个过程叫做回滚(rollback)。为了把为了回滚而记录的东西叫撤销日志(undo log),也可以叫做undo日志。
事务id
先聊聊事务id。
分配事务id的时机
一个事务可以是一个只读事务,也可以是一个读写事务。
事务id是怎么生成的
事务id本质上是数字,分配策略与之前提到的row_id的大致相同,具体如下:
trx_id隐藏列
trx_id就是事务id,之前我们在聊InnoDB行格式的时候重点说过,聚簇索引的记录除了保存完整的用户数据以外,还会自动添加名为trx_id、roll_pointer的隐藏列,如果没有定义主键还会自动添加个row_id的隐藏列。如下图。
roll_pointer
本质上就是一个指针,指向记录对应的undo日志。根据回滚段与undo日志的页号以及offset(undo日志在页面中的偏移量)来定位到具体的日志。
undo日志格式
INSERT操作对应的undo日志格式
如果把数据记录到数据页中,如果希望回滚这个操作,那么只要把这个记录删除就好,也就是说,写对应的undo日志时,只要把这条记录的主键信息记录上就好了,如下所示。
其中undo no在一个事务中从0开始递增。也就是说,只有事务没提交,没生成一条undo日志,值就增1。
注意INSERT的undo日志在事务提交后就直接删除了。
DELETE操作对应的undo日志格式
之前聊过,插入到页面的记录会根据记录头信息中的next_record属性组成单向链表,也是正常记录链表。被删除的记录也会根据next_record组成一个存储空间可被重新利用的链表,也叫垃圾链表。Page Header中有个PAGE_FREE的属性,指向垃圾链表中的头节点。具体的删除过程有两步,如下。
从上面的过程可以看到删除语句过程中,事务提交前只会经历阶段1,也就是delete mark 阶段,所以删除的undo日志只需要考虑阶段1就可以了,具体的数据格式如下。
突然一看这么多,不用担心,我们关注的除了上面说过的那些,额外注意一下trx_id与roll_pointer这两个,当对记录进行delete mark操作前,需要把该记录的trx_id与roll_pointer的旧值记录到undo日志中,像不像一个链表,这就是常说的版本链,如下。
注意DELETE的undo日志在事务提交后还会保留。大家可以想想为什么。
UPDATE操作对应的undo日志格式
具体数据格式如下。
而更新的处理方式有2种。
如果说更新列的值与旧值存储空间大小一致,则会在原纪录的基础上修改。否则,会先将记录从正常的链表中直接移除到垃圾链表中,当事务提交时真正删除,然后在创建新纪录,如果根据页面剩余大小判断是否需要页分裂。
步骤1、将旧纪录进行delete mark操作。此处注意是delete mark操作,也就是事务提交后有专门的线程进行purge操作。
步骤2、根据更新后的数据创建一条新纪录,插入聚簇索引中。
以上增删改对二级索引的影响与更新主键的处理方式相同,只不过单独在操作二级索引的B+树。
注意UPDATE的undo日志在事务提交后还会保留。大家可以想想为什么。
undo日志的存储及使用
存储
存储在类型为FIL_PAGE_UNDO_LOG的页面中。这些页面组成了回滚段,mysql最多支持128个,其中第0号、33-127号针对普通表设计,1-32号针对临时表设计。
使用
在事务执行过程中,最多可以分配4个undo日志链表,分别是:
但是只有真正用到了才会去创建。
undo日志在崩溃恢复时的作用
因为有些没有提交的事务写的redo日志可能也已经刷盘,这些未提交的事务修改的信息也有可能会被恢复。具体的操作是在128个回滚段中找到活跃的undo页面链表,在在undo页面链表中找到对应的事务相关信息,将操作全部回滚掉,保证事物的原子性。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有