事务之undo log

784 阅读3分钟
⚠️

接下来的几篇文章,来好好聊聊 事务和与之相关的日志类型,还有最为重要的 MVCC

undo log 设计的目的是为了记录回滚中的信息。不同类型的操作产生的 undo log 是不同的。既然涉及到事务,就来看看 事务id 是什么东西。

事务id

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

  • 只读事务不能对其他事务也能访问到的表进行 crud 操作,不过可以对临时表进行操作;

    start transaction read only

  • 读写事务是可以表进行 crud 操作;

    start transaction read write

如果一个事务在执行过程中进行了 crud 操作,那么 InnoDB 会给你分配一个独一无二的 事务id

事务id 的生成:server 会在内存中维护一个全局变量,需要一个分配一个,然后 ++id 达到 256 倍数时,会持久化到系统表空间的 Max Trx ID 中;当系统重启时,会把 Max Trx ID 刷新到内存中,将该值 +256 作为全局变量。从而保证了 事务id 的递增

undo log 的格式

为了实现事务的原子性,InnoDB 在每一个 insert,delete,update 的操作,都会把对应的 undo log 记录下来。所以与之对应的每一条 undo log 都会对应一个 undo no

insert

插入一些测试数据,然后来看看在 page 里面发生了什么:

insert into trace(key, typeof) values
("20041", "key"), ("20041.14", "buttonkey")

在一次批量插入语句中,事务是一起进行的,所以紫色块中显示的 事务id 是一样的,但是每条对应的 undo logundo no 是不一样的,因为这需要对应;

而里面的 pointer 就是 roll_pointer,指向的是对应 undo log 的开头地址。

delete

现在开始删除数据:

  1. 删除的时候仅仅把 delete_mask 标志位置位为1,同时也会修改 trx_id, roll_pointer这些隐藏列的值。但是记录还是存在 正常记录链表 中,也就是一个中间状态 ==> mark deleted(用了一下kafka的删除状态标志)。
⚠️

至于为什么没有立即马上直接改变链表结构,这个就和 MVCC 有关了,后面会介绍。

  1. 置位之后,紧接着就会有专门的线程开始操作:

    • 该记录会从 正常记录链表 中删除;
    • 加入 垃圾链表,头插法;
    • 调整page的参数,比如 垃圾链表头 的指针,页面可重用字节数。。。

    以上被称为 purge 阶段。

举个实际例子来说明一下:

-- 显式开启一个事务,假设该事务的id为100
BEGIN;

-- 插入两条记录
INSERT INTO undo_demo(key, type) 
    VALUES ('20041', 'AMM'), ('20041.14', 'KMQ');
    
-- 删除一条记录    
DELETE FROM undo_demo WHERE id = 1; 

来看看中间状态对应的 undo log 结构:

从结构上我们看到 mark deleted 之后对于的 undo log 和之前的 insert 串成一个链表。而这个链表就称为 版本链。【后面讲到的 MVCC 就有这种 版本链

insert 不一样的地方的,有一个 索引列各列信息 。记录的是索引包含的列信息,格式为 <pos,len,value>,【该列在记录的位置,占有的空间大小,实际数据值】。这个部分的信息主要是用在事务提交之后,对 mark deleted 做真正删除的第二步,也就是 purge 阶段。具体如何使用之后可以看到~~~

以上的阶段我们从宏观上观察如下:

update

⚠️

带更新,带制作

本文使用 mdnice 排版