#yyds干货盘点#MySQL学习-为啥有时候事务的隔离没有生效
Posted 汤圆学Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点#MySQL学习-为啥有时候事务的隔离没有生效相关的知识,希望对你有一定的参考价值。
前言
我们知道mysql的事务可以实现语句之间的隔离(ACID中的I),使得各个操作之间互不干扰;
但是有时候我们会发现事务并没有起到隔离的作用;
下面我们就来解释下事务隔离失效的原因以及相关的玩具场景;
目录
- 事务的启动时机
- 事务隔离失效的例子
- 解决办法
正文
1. 事务的启动时机
我们前文了解了事务的启动方式分为显式启动(begin)和隐式启动;
而我们一般采用的启动方式就是显式启动,即通过begin来启动事务;
但是这里的begin
并不会马上启动事务,它只是表示事务的创建时机,真正启动事务是在后续的第一条SQL语句执行时;
这就是为啥有时候明明开启了事务,但是没有起到隔离作用的原因;
当然如果是隐式启动事务,那么就不会存在这个问题;
因为隐式启动就是一条SQL语句执行时自动开启一个事务,然后语句执行完成后自动提交事务,这样在语句执行期间 就不会存在间隙,其他语句也就无法影响到这个语句的执行;
但是隐式启动只能实现隔离性,无法保证操作期间数据的一致性,所以一般不推荐隐式启动;
2. 事务隔离失效的例子
例子的思路就是:
- 开启两个事务
- 然后在第一个事务begin之后,执行第一条SQL语句之前,启动第二个事务,并修改一条数据;
- 再回到事务A去查询这条数据
- 如果查询到的是修改后的数据,则说明事务A没有起到隔离的作用;
下面我们开启两个mysql窗口,并在窗口1中开启一个事务A: begin;
这时事务A并没有启动,begin只是用来创建事务;
接下来我们在窗口2中开启事务B(begin),修改一条数据(之前的name=1,现在改为jalon),提交事务;
这时我们再回到窗口1,去事务A中查询id=1这条数据,会发现查到的是最新的name=jalon
;
这是因为事务A的begin开启事务后并没有马上启动事务,而是在下面的select * from test where id=1;
语句执行时,才真正启动事务;
这时会创建一个视图,用来确保事务A执行期间数据的一致性;
而这个时候,事务B已经修改了id=1的数据,并提交了事务;
所以此时创建的视图会包含最新的数据,即事务B修改过的数据;
3. 解决办法
其实事务的启动有一个快捷方法:start transaction with consistent snapshot
;
这个命令的意思就是:创建+启动事务,相当于begin命令以及begin到第一条SQL语句执行之间的间隙;
这时我们再重复上面的例子,不过不是以begin的形式开启事务,而是快捷命令start transaction with consistent snapshot
:
下面的窗口1先创建并启动事务A,然后窗口2创建事务B,修改数据,提交事务B;
然后再回到窗口1查询数据;
可以看到,查询的数据还是旧的数据(新数据为name=ling
);
这说明事务A的隔离生效了,这就是start transaction with consistent snapshot
的功劳;
总结
- 事务隔离失效的原因:
- 因为事务的begin只是创建事务,而真正启动事务是在第一条SQL语句执行时;
- 如果在begin和执行第一条SQL语句期间,其他事务修改了数据,那么事务A就会读到最新的数据,而不是begin时的数据;
- 事务隔离失效的解决:
- 使用创建并启动事务的快捷命令,
start transaction with consistent snapshot
; - 如果隔离级别是读提交RC,那么该命令就无效了,因为只要一个事务提交了更新,其他事务都会读到最新的数据,而不是事务启动时的数据
- 使用创建并启动事务的快捷命令,
- 事务视图的创建时机:
- 如果是begin命令,则是在第一条SQL语句执行时创建一致性视图
- 如果快捷启动命令
start transaction with consistent snapshot
,则该命令执行时就会创建一致性视图
以上是关于#yyds干货盘点#MySQL学习-为啥有时候事务的隔离没有生效的主要内容,如果未能解决你的问题,请参考以下文章