结合前面提到的LBCC,和今天的MVCC,是实现事务隔离级别的两种方式
但,怕是有些兄弟误会,他们两个是共同来维护mysql的一个隔离级别的,可以说是使用与不同的情况,并不是彼此替代的请情况
undo log、redo log;
当前读、快照读
关于事务这块的我自己的思维导图 和大家分享一下
Multiversion concurency control(多版本并发控制)
并发访问(读或写)数据库时,对正在事务内处理的数据做多版本的管理。以达到用来避免写操作的堵塞,从而引发读操作的并发问题。
我想大多数的可能就直接抛结论了,直接undo,redo就开始抛了,但我这需要先和大家聊些别的。
其实在每个表中都会加有隐藏字段,其中这里用到的有两个:
DB_TRX_ID(数据行的版本号)、DB_ROLL_PT(删除版本号)
还有一点需要提前说的是,系统有一个全局的事务id,每开启一个事务,都有他自己对应的事务id,自增。
然后下面会以一个流程的的形式对表进行几条数据的操作,想了解的,最好不要跳过
插入
首先我们有一个空表teacher,表结构如图
然后开启一个事务,插入两条数据,并提交。假设现在的全局事务id为1,
新插入数据的 行版本号为当前事务的id,删除版本号为null。
删除
再次开启一个事务,删除id为2的数据,并提交。假定此时的全局事务id为22,
删除的数据 的删除版本号修改为当前的事务id
更新
再次开启事务(开不开的吧,反正都会自动开启并提交),将id为1的数据的name修改为19
假定此时的事务id已经到了33
那么他会先将这条数据copy一份,修改原始数据的 删除版本号 为当前的事务id
新的数据的 行版本号 为当前的事务id,并修改对应的数据
查询
假定当前的事务id已经到了44。
然后在其查询的时候是存在一些查询规则的:
(如果感觉这个规则不太明白的,可以在重新过一遍上面的增删改;
如果感觉已经明白了的,可以继续向下看案例,可能会让你又不明白了。)
1、查找数据行版本号等于或早于当前事务版本号的数据行
也就是说,在我查询的时候,这条数据已经存在了,或者是在我本事务中select之前的数据修改
2、查找删除版本号要么为null,要么大于当前事务版本号的记录。
也就是说,在我开启事务并查询的时候,这条数据还没有被删除。
但其实呢,这样的MVCC,还是有一定的问题的。
我们从下面的两个案例来分析一波:
首先准备两条数据,然后两个事务并发执行
1234,分别表示两个事务中的开始和执行查询的语句,在两个案例中均未执行提交操作
案例一(1,2,3,4,2)
1、先插入就两条数据,行版本号 = 事务id=1
2、开启一个事务,并执行查询,事务id=2时查询到了两条数据,
3、开启一个事务,并执行修改操作(这个动作不明白的参考上面的规则),此时的事务id=3
4、再次执行事务id=2中的查询操作,查询到的数据还是原来的数据
1)查找数据行版本号等于或早于当前事务版本号的数据行,抛弃表格中的 第三条数据。
2)查找删除版本号要么为null,要么大于当前事务版本号的记录。两条数据都符合,不变,得到两条数据
通过第一个案例,我们发现,MVCC的规则,没毛病。
案例二(3,4,1,2,)
1、先插入就两条数据,行版本号 = 事务id=1
2、开启一个事务,执行修改操作,此时的事务id=2
3、开启一个事务,执行查询操作,此时的事务id=3
1)查找数据行版本号等于或早于当前事务版本号的数据行,全部保留
2)查找删除版本号要么为null,要么大于当前事务版本号的记录。抛弃第一个数据,得到后两条数据。
但,此时就有了些问题。
因为此时咱们两个事务是并发执行的,都还没有进行提交操作,我第二个事务读到了第一个事务的操作,这显然非常不合理。
先来抛一些概念,能理解的最好,不理解的可以结合下面的图来进一步吸收一下
Undo Log
undo log 是在事务开始前,在操作任何数据之前,首先将需要操作的数据备份到一个地方(Undo Log)
实际上,undo log是为了实现事务的原子性而出现的一个产物。
事务在执行的过程中,如果出现了错误或者用户执行rollback语句时,mysql可以利用undo log中备份的信息将数据恢复到事务开始之前的状态。
在innodb引擎中,undo log被借用来实现 多版本并发控制
事务未提交前,undo保存了未提交之前的版本数据,undo中的数据可作为数据旧版本快照供其他并发事务进行快照读。
Redo Log
redo log 是在事务中操作的任何数据,将最新的数据备份到一个地方(Redo Log)
redo log的持久
redo log的持久不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo中。具体的落盘策略可以进行配置
redo log是为了实现事务的持久性而出现的产物
防止在发生故障的时间点,尚有赃页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的未入磁盘数据进行持久化这一特性
快照读
快照读的 sql读取的数据就是快照版本,也就是历史版本,普通的select就是快照读
innodb快照读,数据的读取将由cache(原本数据)+ undo(事务修改过的数据)两部分组成
当前读
当前读的 sql读取的数据是最新版本。通过锁机制来保证读取数据无法通过其他事务进行修改
UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE 都是当前读
下面咱们来看图吸收一下上面的东西。
当我们需要对某一个表进行操作的时候,会先将teacher.ibd的数据加载到内存(cache)中。
当我们开启一个事务之后,会将当前事务涉及到的数据,备份到undo buffer中,如果需要回滚的时候,会通过buffer恢复到teacher中。
Hello World