MySQL - 当前读和快照读

当前读和快照读

InnoDB 给每一个事务生成一个唯一事务 ID 的方法称为生成快照,因此这种场景称为快照读

但是对于更新数据不能使用快照读,因为更新数据时如果使用快照读会可能会覆盖其他事务的更改。

另外查询时如果加锁也会采用当前读的方式。当前读就是读这个数据最新的提交数据。

InnoDB 的多版本并发控制实现了在串行化的隔离级别下读不加锁,提高了并发性能。

当前读

当前读的场景有下面几种:

update ... (更新操作)

delete ... (删除操作)

insert ... (插入操作)

select ... lock in share mode (共享读锁)

select ... for update (写锁)

当前读,读取的是最新版本,并且对读取的记录加锁,阻塞其他事务同时修改相同记录,避免出现安全问题。

例如,假设要update一条记录,但是另一个事务已经delete这条数据并且commit了,如果不加锁就会产生冲突。所以update的时候肯定要是当前读,得到最新的信息并且锁定相应的记录。

快照读

快照读的场景:

单纯的select操作
(不包括上面当前读的select ... lock in share mode,select ... for update)

Read Committed隔离级别:每次select都生成一个快照读

Read Repeatable隔离级别:开启事务后第一个select语句才是快照读的地方,而不是一开启事务就快照读

例子

下面通过一个例子来理解快照读和当前读:

首先建一个表 t,并插入一条数据。

mysql-> create table t(k int)ENGINE=InnoDB;
mysql-> insert into t(k) values (1);

然后将事务的隔离级别设置为 REPEATABLE-READ,接着开启三个事务,并按照下面的顺序进行执行。

MySQL 中事务开始的时间

一般我们会认为 begin/start transaction 是事务开始的时间点,也就是一旦我们执行了 start transaction,就认为事务已经开始了,其实这是错误的。事务开始的真正的时间点(LSN),是 start transaction 之后执行的第一条语句,不管是什么语句,不管成功与否

但是如果你想要达到将 start transaction 作为事务开始的时间点,那么我们必须使用:

start transaction with consistent snapshot

它的含义是:执行 start transaction 同时建立本事务一致性读的 snapshot . 而不是等到执行第一条语句时,才开始事务,并且建立一致性读的 snapshot .

效果等价于: start transaction 之后,马上执行一条 select 语句(此时会建立一致性读的snapshot)。

事务A 事务B 事务C 说明
start transaction with consistent snapshot 开启事务A
start transaction with consistent snapshot 开启事务B
select k from t; 事务A读取结果是1(快照读)
select k from t; 事务B读取结果是1(快照读)
update t set k = k + 1; 事务C提交的k为2
update t set k = k + 1; update 语句进行了一次当前读将 k 的值更新为事务 C 已经提交的结果 2,并且在此基础上再加1得到3
select k from t; 执行了 update 操作时会创建一个新版本的数据,并且将自己的事务 ID 作为该数据的版本号,因此在该事务内可以读到自己更新的数据。因此事务 B 最后一次查询的结果是 3。
commit; 事务B提交
select k from t; 事务A读取结果是1(快照读)
commit; 事务A提交
select k from t; 读取结果是 3

图示:

MySQL - 当前读和快照读

参考
https://juejin.im/post/6844903928627200007
https://www.cnblogs.com/digdeep/p/4947694.html

上一篇:Mybatis事务创建和管理


下一篇:Spring 事务 注解@Transaction 用法