研究mysql45讲时的又一个实验,灵感来自08讲的思考题。多session下可能会造成的不一致问题。过程如下
实验条件:Mysql,innoDB
表t初始数据如下
| id | c |
Line1 | 1 | 1 |
Line2 | 2 | 2 |
Line3 | 3 | 3 |
Line4 | 4 | 4 |
实验步骤如下
时刻 | Session1 | Session2 |
1 | start transaction with consistent snapshot; /*开启事务并在开启时开一致性视图*/ |
|
2 |
| Update t set c=c+1; /*在另一个session中把表更新了,此时表中存储的c值:2,3,4,5*/ |
3 | Select * from t; /*由于一致性视图,后面更新的内容在之前的session中是不认账的,这里查到的c值仍为1,2,3,4*/ |
|
4 | Update t set c=0 where c=3; /*概念1:“当前读”,虽然使用了可重复读隔离级别,但为了防止数据增删改操作丢失,这些操作会先读取最新版本的值而后在最新值上直接进行操作,因此被修改的实际上是line2 (2,3)->(2,0) */ |
|
5 |
| Select * from t; /*严格来说这里用这一句相当于又开了一个session,该算作session3的,不好排版所以搁这儿了 此时c值:2,3,4,5因为session1还没提交。假设再在session1外有其他update操作,那么由于session1使用update操作过了这张表,两阶段锁,排队等待session1提交或回滚。(这个update无论想改的是line2还是line1,3,4都得等着)*/ |
6 | Select * from t; /*概念2:一致性视图对于后面更新的内容虽然不认账,但对于自己更新的内容是要认的,即是说line1,3,4仍然保持开启事务时的一致性读状态,而自己修改过的line2则是最新状态。 此时查询到的c值为1,3,3,4 */ |
|
具体结果也截个图吧
附上原问题与我觉着很赞的一篇分析过程
我用下面的表结构和初始化语句作为试验环境,事务隔离级别是可重复读。现在,我要把所有“字段 c 和 id 值相等的行”的 c 值清零,但是却发现了一个“诡异”的、改不掉的情况。请你构造出这种情况,并说明其原理。
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, c) values(1,1),(2,2),(3,3),(4,4);
@lucky star 7
答案:
分析: 假设有两个事务A和B, 且A事务是更新c=0的事务; 给定条件: 1, 事务A update
语句已经执行成功, 说明没有另外一个活动中的事务在执行修改条件为id in 1,2,3,4或c in 1,
2,3,4, 否则update会被锁阻塞; 2,事务A再次执行查询结果却是一样, 说明什么?说明事务
B把id或者c给修改了, 而且已经提交了, 导致事务A“当前读”没有匹配到对应的条件; 事务
A的查询语句说明了事务B执行更新后,提交事务B一定是在事务A第一条查询语句之后执行
的;
所以执行顺序应该是:
1, 事务A select * from t;
2, 事务B update t set c = c + 4; // 只要c或者id大于等于5就行; 当然这行也可以和1调换,
不影响
3, 事务B commit;
4, 事务A update t set c = 0 where id = c; // 当前读; 此时已经没有匹配的行
5, 事务A select * from t;