Mysql Innodb中undo-log和MVCC多版本一致性读的实现(源码分析)

Mysql Innodb中undo-log和MVCC多版本一致性读  的实现

   
转自:http://blog.sina.com.cn/s/blog_4673e603010111ty.html  
本文主要介绍mysql中innodb引擎undo-log和事务中MVCC多版本一致性读的实现。

1. 概述
Innodb引擎的undo日志是记录在表空间中单独的回滚段中。 当mysql做update和delete操作的时候,实际的后台都是先把旧记录“删”了,如果是update和insert再把新记录“插入”进去。

这里的删不是真的删除,而是标识它被删除了。而插入也不一定是真的插入,很多情况下是原地覆盖原来的记录。

而关于MVCC多版本一致性读,就是在同一个事务中,用户只能看到该事务之前已经生效的和该事务本身做的修改。
在innodb中“MVCC多版本一致性读”功能的实现是基于undo-log的。

Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
                                                                                                                                           图1
图1描述了innodb中undo-log和“MVCC多版本一致性读”功能的基本实现。
Innodb引擎的主键索引记录的头上包含有6字节的事务ID(DB_TX_ID)与7字节指向回滚段中旧版本的指针(DB_ROLL_PTR)。
当发生修改的之前,innodb会把被修改的字段的原始版本值和它们对应的版本号写入回滚段中,而指针DB_ROLL_PTR指向的是最新的undo日志。

当一个事务被创建的时候,innodb会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,innodb就会从根据 read view,来决定是读取该行记录的当前版本,还是需要从undo-log中去寻找更早的版本。

2.undo-log
为了简化分析,关于undo-log本文只介绍如下这种情况,也是最普遍的一种情况:
一条update语句,它根据主键进行查找,并且不修改主键的值
涉及到的代码入口在 storage/innobase/btr/btr0cur.c 的btr_cur_update_in_place函数 (1821行)
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
首先记录undo-log,把本次修改的字段原始值记录下来:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
然后在本条记录上进行修改:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
修改后写redo-log,redo-log是单独存放的,存放在名为ib_logfile的一组文件中:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
可以看出以上大体的流程就是先写undo-log,然后本地修改,最后写redo-log。

写undo-log的函数btr_cur_upd_lock_and_undo最终会调用函数trx_undo_page_report_modify(或insert)。 (storage/innobase/trx/trx0rec.c 529行)。
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
把旧版本的事务id写入undo-log:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
把该行的标识字段写入undo-log:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
将旧值保存的undo-log中:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现

3. MVCC多版本一致性读
在innodb中,创建一个新事务的时候,innodb会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,innodb会将该行当前的版本号与该read view进行比较。
具体的算法如下:
1. 设该行的当前事务id为trx_id_0,read view中最早的事务id为trx_id_1, 最迟的事务id为trx_id_2。
2. 如果trx_id_0< trx_id_1的话,那么表明该行记录所在的事务已经在本次新事务创建之前就提交了,所以该行记录的当前值是可见的。跳到步骤6.
3. 如果trx_id_0>trx_id_2的话,那么表明该行记录所在的事务在本次新事务创建之后才开启,所以该行记录的当前值不可见.跳到步骤5。
4. 如果trx_id_1<=trx_id_0<=trx_id_2, 那么表明该行记录所在事务在本次新事务创建的时候处于活动状态,从trx_id_1到trx_id_2进行遍历,如果trx_id_0等于他们之中的某个事务id的话,那么不可见。跳到步骤5.
5. 从该行记录的DB_ROLL_PTR指针所指向的回滚段中取出最新的undo-log的版本号,将它赋值该trx_id_0,然后跳到步骤2.
6. 将该可见行的值返回。

关于如何判断当前系统活跃事务列表中的事务是否应该进入read view(即对当前事务不可见),可以参考read view的创建代码: storage/innobase/read/read0read.c的函数read_view_open_now。
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
可以看出主要是排除了当前事务自己,以及目前正在内存中提交的事务。

关于判断记录当前值是否可见的代码位于storage/innobase/include/read0read.ic的函数read_view_sees_trx_id,图中的view->up_limit_id就是上面的trx_id1, view->low_limit_id就是上面的trx_id2.
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现
下面是关于low_limit_id和up_limit_id的定义(它们的名字看起来容易产生误解。。。):
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现


4. 后记
关于写redo-log的代码入口在:storage/innobase/log/log0log.c 的函数log_group_write_buf(1227行)。而关于redo-log相关函数的使用介绍在该文件的开始部分:
Mysql <wbr>Innodb中undo-log和MVCC多版本一致性读 <wbr> <wbr>的实现


参考:
http://www.ningoo.net/html/tag/mysql
http://wangyuanzju.blog.163.com/blog/static/130292009107101544125/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值