1 演示错误案例
先给大家来一个错误演示。
我们打开两个会话窗口,默认情况下隔离级别是可重复读,我们来看下:
首先在 A 会话中查看当前 user 表,查看完成后开启事务:
可以看到id=3的数据sex是男。
接下来在 B 会话中修改 sex:
查看修改成功
接下来回到A会话查询:
可以看到,A 会话的记录也变了。完整测试流程如下:
一致性问题 | |
---|---|
A会话 | B会话 |
select * from user; | |
begin; | |
update user set sex='女' where id = 3; | |
select * from user; |
说好的可重复读呢?
按理说,可重复读就是别的事务对数据的操作不影响当前事务,但是上面这个案例似乎和我们理解的可重复读有出入。
2 分析
不知道小伙伴们是否还记得可重复读的特点:
用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。
从这个角度来说,第一小节的案例似乎也没有问题,因为我们在 A 会话中执行 SELECT 语句多次,查到的结果也都是相同的,sex 都是 女。
但是我们疑惑的是明明 B 会话的事务后开启的,但是我们却在 A 会话中读取到了 B 的修改,这似乎不应该。
这里就涉及到一个问题,事务的一致性视图是何时建立的?
事实上,我们执行的 begin 语句并不是一个事务真正的起点。执行完 begin 之后,接下来执行的第一句 SQL,事务才真正启动。
我们稍微修改一下第一小节的案例:
一致性问题 | |
---|---|
A会话 | B会话 |
select * from user; | |
begin; | |
select * from user; | |
update user set sex='男' where id = 3;(之前已经改成了女) | |
select * from user; |
A会话结果如下:
B会话结果如下:
在 A 会话中,事务开启之后,立马先执行一条 SELECT 语句,然后再去 B 会话中做修改,修改完成后再回到 A 会话继续查询,此时发现 B 中的修改对 A 并不可见,这个结果也符合用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。
如果我们想要执行完 begin 之后,就立马开启事务,那么可以通过如下方式来执行:
start transaction with consistent snapshot;
这个 SQL 执行完之后,事务立马就启动了。
接下来,回到第一小节的案例,我们修改一下事务启动的命令:
一致性问题 | |
---|---|
A会话 | B会话 |
select * from user; | |
start transaction with consistent snapshot; | |
update user set sex='女' where id = 3; | |
select * from user; |
此时,A 会话中事务的查询就看不见 B 中的修改了。