mysql 快照读

原文地址: https://blog.lvcc.top/article-detail?articleId=351

问题引入

有次小A问我,他有一段代码,明明加了分布式的锁,但有时候仍然插入了两条数据,是为什么?
已知数据库为mysql, 引擎为innodb, 隔离级别为Repeatable Read, 他的伪代码如下:

@Transcational
public void checkAndInsert(){
       Object a = executeQuery("select * from table_a");
       if(a==null){
              distribute_lock();
              a = executeQuery("select * from table_a");
              if(a == null){
				executeUpdate("insert into table_a ...");
			}
			distribute_unlock();
	}
}

首先他在函数外面开启了事务,然后查询一次有没有数据,如果没有就加锁,再查询一次,还没有就插入数据,看起来没有什么问题,但为什么并发执行会插入两条数据呢? 这就不得不提到mysql数据库的一致性读问题了。

地球人都知道,mysql的innodb引擎使用了MVCC, 对于事务中的查询,在隔离级别Repeatable Read和Read Committed下面,它提供了一致性读的功能(快照读)。详见官方文档: https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html

定义

mysql有两种读数据的模式,一种是当前读,一种是快照读。
快照读的意思是,数据有多个版本, 当事务并发执行时, 某一事务读取的数据来自其中一个版本(快照)。下面举例说明(Repeatable Read级别):

时间事务A事务B
0set autocommit=0 (开始事务)set autocommit=0(开始事务)
1selcect * from table_a where name=‘abc’ (返回空)
2insert into table_a(name) values(‘abc’)
3commit
4selcect * from table_a where name=‘abc’ (仍然空)

可以看到,两个并发执行的事务, 即使B插入并提交了数据, A仍然看不到,因为A读的还是快照。

时间点

事务在查询的时候,是查找某一个快照的,那么怎么确定查的是哪一个快照呢? 这个时间点,就是事务第一次查询时的时间,在这个时间点前面提交的数据(其他事务提交),都可以查询到。
像上面那个例子,就是因为事务A第一次查询时间早于事务B提交时间,所以查询不到aaa的数据。下面再看一次例子:

时间事务A事务B
0set autocommit=0 (开始事务)set autocommit=0(开始事务)
1insert into table_a(name) values(‘bbb’)
2commit
3selcect * from table_a where name=‘bbb’ (返回了数据)

可以看到事务A和B同时开始,但由于事务A的第一次查询时间晚于B, 所以能查到B提交的数据。
如果在事务中使用了DML更新/删除了其他事务提交的数据,那么这些数据会对当前事务可见,可以查询到,注意仅仅是被影响到的数据,其他在这之前提交的数据一样查询不到。

对插入两条数据问题的解释

来看下可能插入两条数据的流程:

时间事务A事务B
0set autocommit=0 (开始事务)set autocommit=0(开始事务)
1Object a = executeQuery(“select * from table_a”) (返回空)
2Object a = executeQuery(“select * from table_a”) (返回空)
3lock 成功
4lock 等待a = executeQuery(“select * from table_a”)(返回空)
5executeUpdate(“insert into table_a …”) (插入成功)
6unlock
7lock成功commit
8a = executeQuery(“select * from table_a”) (由于A的时间点为1,所以查询不到最新数据,返回空)
9executeUpdate(“insert into table_a …”) (插入成功)
10unlock
11commit
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值