这几天在开发过程中遇到一个很棘手的问题,两个不同应用环境中同一块代码产生两种不同的现象,有两个事务,第一个事务进行修改操作并且提交事务,第二个事务进行查询,但查询到的是修改前的数据,刚开始以为是Mybatis一级缓存造成的,但是通过修改sql排除了缓存这种情况,后面确认是数据库的事务隔离级别导致的,因为两个数据库的事务隔离级别分别是:read committed、repeatable read,隔离级别是repeatable read的会出现上述情况,下面通过对比例子详细介绍下:
Mysql数据库的事务隔离级别
1、read uncommitted(读未提交)
可以读取到未提交的数据,如果事务A进行修改操作并没有进行提交,但是事务B就已经会读取到修改后的新数据,这时如果事务A因为某种原因回滚,但是事务B不会回滚,读取到的还是修改后的数据,因此这种事务级别会产生大量的脏数据
2、read committed(读已提交)
mysql默认的事务隔离级别,这种事务级别不会读取到脏数据,相对于read uncommitted级别,它在A事务修改操做未提交之前,事务B读取到的是修改前的数据,只有当A事务提交之后,事务B读取的才是修改后的数据
3、repeatable read (可重复读)
这种事务隔离级别相对于read committed事务隔离级别会产生这种现象,如果事务B之前执行过查询语句,当A事务修改操作提交之后,事务B查询读取到的仍是修改前的数据,这一现象说明,如果事务B之前执行过相同的sql语句,下次执行同样的sql,不管事务A做了什么操作,事务B会重复读取原有的数据结果;如果开启的事务B首先不做任何查询操作,当A事务的修改操作提交之后,这时事务B才会查询到修改后的数据
4、serializable(串行化)
这种事务隔离级别是最高的级别,只有当事务A的写操作执行完之后,事务B才能进行写操作,但是读操作是可以正常进行的,这就相当于给这条操作的数据加了锁
事务隔离级别的基本操作
1、查看当前会话的事务隔离级别
select @@tx_isolation
2、查看系统的事务隔离级别
select @@global.tx_isolation
3、设置当前会话的事务隔离级别
set session transaction isolation level 事务隔级别
4、设置系统的事务隔离级别
set global transaction isolation level 事务隔离级别
本地测试验证
一共有四种事务隔离级别,这里只针对于开发过程中遇到的由于read committed、repeatable read这种两种隔离级别导致的问题进行本地的验证
1、read committed (读已提交)
(1)、打开两个mysql数据库客户端A、B,因为默认是read committed级别,所以不需要设置,两个客户端开启事务A、B,并执行查询语句
(2)、在事务A中进行修改操作,不要提交,事务A中查询结果已修改,但是事务B中查询到的是修改前的数据,两个客户端查询结果
(3)、事务A进行提交,事务B查询到的是修改后的数据
2、repeatable read(可重复读)
(1)、设置系统的事务隔离级别为:repeatable read
(2)、开启事务A、B,并且执行同样的查询语句
(3)、事务A中进行修改操作,并且提交,在事务B中执行查询语句,查询到是却还是修改前的数据,因此这种事务会导致:如果执行相同的查询语句,则不管事务A做了什么操作,则会重复读取原来读取数据
(4)、如果在上面步骤二中,事务A中进行查询,然后进行修改提交,事务B开启事务后先不进行查询,等事务A的修改操作提交之后,事务B再进行查询操作,则查询到的是修改后的数据
测试结论
1、read committed (读已提交)这种事务隔离级别,不管事务A做什么操作,只有当事务A提交之后,事务B才会读取到新的数据,如果事务A不提交,则事务B不会读取到新数据
2、repeatable read(可重复读)这种事务隔离级别,如果事务B在事务A提交之前做了查询操作,然后不管事务A做了什么操作并且提交后,事务B如果执行同样的查询语句,则不会读取到修改后的新数据,会一直重复读取刚开始事务B读取的数据;如果事务B在事务A提交之前不做任何查询操作,则不管事务A做了什么操作并且提交后,事务B查询到的才是修改后的新数据