侧边栏壁纸
  • 累计撰写 79 篇文章
  • 累计创建 84 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

MySQL学习-为啥有时候事务的隔离没有生效

汤圆学Java
2022-02-07 / 0 评论 / 0 点赞 / 68 阅读 / 2,452 字
温馨提示:
本文最后更新于 2022-02-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

我们知道mysql的事务可以实现语句之间的隔离(ACID中的I),使得各个操作之间互不干扰;

但是有时候我们会发现事务并没有起到隔离的作用;

下面我们就来解释下事务隔离失效的原因以及相关的玩具场景;

本文是基于默认的 可重复读(RR)隔离级别 来介绍的;

目录

  1. 事务的启动时机
  2. 事务隔离失效的例子
  3. 解决办法

正文

1. 事务的启动时机

我们前文了解了事务的启动方式分为显式启动(begin)和隐式启动;

而我们一般采用的启动方式就是显式启动,即通过begin来启动事务;

但是这里的begin并不会马上启动事务,它只是表示事务的创建时机,真正启动事务是在后续的第一条SQL语句执行时;

这就好比流水线操作,先去打开电源,然后进行后续的流水线操作,真正的启动其实就是第一个流水作业启动时;

这就是为啥有时候明明开启了事务,但是没有起到隔离作用的原因;

当然如果是隐式启动事务,那么就不会存在这个问题;

因为隐式启动就是一条SQL语句执行时自动开启一个事务,然后语句执行完成后自动提交事务,这样在语句执行期间 就不会存在间隙,其他语句也就无法影响到这个语句的执行;

但是隐式启动只能实现隔离性,无法保证操作期间数据的一致性,所以一般不推荐隐式启动;

2. 事务隔离失效的例子

例子的思路就是:

  • 开启两个事务
  • 然后在第一个事务begin之后,执行第一条SQL语句之前,启动第二个事务,并修改一条数据;
  • 再回到事务A去查询这条数据
  • 如果查询到的是修改后的数据,则说明事务A没有起到隔离的作用;

下面我们开启两个mysql窗口,并在窗口1中开启一个事务A: begin;

image-20220207211851404

这时事务A并没有启动,begin只是用来创建事务;

接下来我们在窗口2中开启事务B(begin),修改一条数据(之前的name='1',现在改为'jalon'),提交事务

image-20220207212837507

这时我们再回到窗口1,去事务A中查询id=1这条数据,会发现查到的是最新的name=jalon

image-20220207212744558

这是因为事务A的begin开启事务后并没有马上启动事务,而是在下面的select * from test where id=1;语句执行时,才真正启动事务;

这时会创建一个视图,用来确保事务A执行期间数据的一致性;

而这个时候,事务B已经修改了id=1的数据,并提交了事务;

所以此时创建的视图会包含最新的数据,即事务B修改过的数据;

如果事务B只是修改了数据,但是没有提交,那么此时的视图还是旧的数据(因为事务B的修改还没有同步到数据库)

3. 解决办法

其实事务的启动有一个快捷方法:start transaction with consistent snapshot

这个命令的意思就是:创建+启动事务,相当于begin命令以及begin到第一条SQL语句执行之间的间隙

这时我们再重复上面的例子,不过不是以begin的形式开启事务,而是快捷命令start transaction with consistent snapshot:

下面的窗口1先创建并启动事务A,然后窗口2创建事务B,修改数据,提交事务B;

image-20220207214046577

然后再回到窗口1查询数据;

image-20220207214127994

可以看到,查询的数据还是旧的数据(新数据为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,则该命令执行时就会创建一致性视图
0

评论区