前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试必会之事务如何保证原子性-undo日志

面试必会之事务如何保证原子性-undo日志

作者头像
热心的大肚皮
发布2023-02-28 14:01:55
6930
发布2023-02-28 14:01:55
举报
文章被收录于专栏:程序猿日常笔记

大家好,我是热心的大肚皮,皮哥。

undo日志的前世

之前聊过,事务需要保证原子性,要么全部完成,要么什么也不做,但是经常会出现事务在执行到一半时会出现情况,例如:

  • 服务器本身的错误、操作系统错误、突然断电等。
  • 可以在事务执行过程中手动输入ROOLBACK来结束事务。

事务执行到一半就结束,但是事务在执行过程中可能修改了很多东西。为了保证原子性,需要改回原来的样子,这个过程叫做回滚(rollback)。为了把为了回滚而记录的东西叫撤销日志(undo log),也可以叫做undo日志。

事务id

先聊聊事务id。

分配事务id的时机

一个事务可以是一个只读事务,也可以是一个读写事务。

  • 对于只读事务来说,只有在它对一次对用户创建的临时表执行增删改操作时,才会分配个事务id,否则不会分配。
  • 对于读写事务来说,只有在它第一次对某个表执行增删改操作时,才会分配事务id,否则不会。

事务id是怎么生成的

事务id本质上是数字,分配策略与之前提到的row_id的大致相同,具体如下:

  • 服务器内部维护个全局变量,当哪个事务需要分配时,就把当前值分配给当前事务,并且变量值自增1。
  • 当变量值为256的倍数时,刷新到系统表空间的Max Trx ID的属性中。
  • 重启时将Max Trx ID 加载到内存中,并且将该值加上256,主要为了防止关机时该值未刷新磁盘中。

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的属性,指向垃圾链表中的头节点。具体的删除过程有两步,如下。

  • 仅将记录的deleted_flag标识位设置为1,并没有移动到垃圾链表中,其他也不做修改,这个过程叫delete mark。
  • 当删除语句所在的事务提交时,会有专门的线程把记录从正常链表移除,并且加入到垃圾链表中。这个阶段叫purge。

从上面的过程可以看到删除语句过程中,事务提交前只会经历阶段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日志链表,分别是:

  • 针对普通表的insert undo链表;
  • 针对普通表的update undo链表;
  • 针对临时表的insert undo链表;
  • 针对临时表的update undo链表;

但是只有真正用到了才会去创建。

undo日志在崩溃恢复时的作用

因为有些没有提交的事务写的redo日志可能也已经刷盘,这些未提交的事务修改的信息也有可能会被恢复。具体的操作是在128个回滚段中找到活跃的undo页面链表,在在undo页面链表中找到对应的事务相关信息,将操作全部回滚掉,保证事物的原子性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿日常笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档