undo log多版本链实现ReadView机制 undo

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

undo log版本链

undo log是用来记录事务回滚之前的操作数据,在每条数据都有两个隐藏字段,trx_id、roll_pointer。

  • trx_id:最近一次更新这条数据的事务ID
  • roll_pointer:指向这个事务生成之前的undo log

有了这个字段,这样的话undo log 对于同一条数据的日志记录,并不是只有一条,而是不同的事务操作都进行了记录,这样的话就用链表链接起来。下面通过一个具体的案例来了解下undo log版本链。

在最初的时候,有事务A(事务id为:10)插入一条数据值为A,那么这条数据的隐藏字段以及指向的undo log如下图所示:

image.png

插入的数据值为A,事务di为10,由于是第一次插入,则版本链roll_pointer指向一个空的undo log。
接着事务B对这条数据进行了修改,将值更为是B, 事务ID为20,那么此时更新之前会生成一个undo log记录之前的值,然后会让roll_pointer指向这个实际的undo log回滚日志,如下图所示:

image.png

这个是时候事务C又对值进行了修改,将值更改为C,事务ID是30,那么也会生成一个记录,如下图所示:

image.png

从上面的示意图可以看出,多个事务串行执行的时候,每个事务修改数据后,都会更新隐藏字段txr_id和roll_pointer,同时将之前修改的undo log日志通过roll_pinter指针串联起来,形成一个重要的版本链。

多版本链路ReadView机制

在执行一个事务的时候,会生成一个ReadView,有四个比较重要的东西:

  • m_ids:此时有哪些事务在MySQL里执行还没提交
  • min_trx_id:m_ids里最小的值;
  • max_trx_id:mysql下一个要生成的事务id,就是最大事务id
  • creator_trx_id:当前这个事务的id

接着上面的例子,在数据库已经有一行数据,事务id是30,初始情况如下图:
image.png

此时有两个事务并发执行,事务A(id=40),事务B(id=50),事务B更新数据,事务A查询数据,如下图所示:

image.png

此时事务A开启一个ReadView,这个ReadView里的m_id包含事务A和事务B的两个id(40,50),min_trx_id=40,max_trx_id=60(假设步长10),creator_trx_id=45(事务A自己)
这时候事务A查询数据的时候,会判断一下当前这行数据的txr_id是否小于ReadView中的min_trx_id,此时发现txr_Id=30,是小于ReadView里的min_trx_id,那么认为在事务开启之前,修改这行数据的数据早就提交,此时可以查询到这行数据。如下如所示:

image.png

此时事务B也开启执行,把这行数据修改为值B,然后这行数据txr_id设置为自己的id(50),同时roll_pointer指向修改之前的undo log,事务B提交事务执行成功如下图所示:

image.png

这个时候事务A再次查询,就会发现此时txt_id=50,此时的txr_id是大于ReadView里的min_txr_id=40,同时小于ReadView里的max_txr_id= 60,说明这条数据在事务A开启的时候发生的修改,此时的txr_id=50 也在m_ids(40,50)中,那么说你这条数据的事务是事务A同一时段并发执行然后提交的,所以对这样数据是不能查询的,如下如图所示:

image.png

这时候事务A在查询的时候会根据数据roll_pointer顺着undo log日志链表往下找,会找到最近一条undo log,trx_id=30,此时发现小于ReadView中的min_txr_id=40,说明这条数据的版本是在事务A开启之前执行提交的。
在接着假设事务A查询完数据后又对这行数据的值修改,改成了值A,此时的trx_id=40,同时保存修改之前值的快照链,如下图所示:

image.png

此时事务A再次查询的时候会发现最新的数据是trx_id=45,是跟自己的trx_id一样,那说嘛这行数据是自己修改的,是可以查询的。

image.png

再接着在事务A的期间,又来个是事务D,此时的事务id是60,然后将数据更新值为D,并且成功提交,此时的undo log版本链如下图所示:

image.png

此时事务A再次查询的时候,发现大于自己的事务ID,那么说明这数据是事务A开启之后,然后又有事务来更新了数据,是不允许查询的,此时会顺着undo log版本链往下找,就会找到值A自己更新的那条数据。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%