再谈数据库隔离级别

前言

在“数据库事务和事务的隔离级别”一文中,事务的隔离级别有如下4中隔离级别,

1.未授权读取,read uncommitted

2.授权读取,read committed

3.可重复读取,repeatable read

4.串行化,serializable

这次我以mysql为例,通过实际操作演示一下这四种隔离级别。

笔者使用的mysql版本是5.6.41,我们可以在客户端调用version()用来查看数据可的版本。

我们通过客户端通过@@tx_isolation查看一下mysql默认设置的事务隔离级别是REPEATABLE-READ

事务隔离级别设置

在“数据库事务和事务的隔离级别”,笔者已经提出了当前会话事务隔离级别和全局事务隔离级别。

更改当前会话事务隔离级别:set session transaction isolation level serializable

更改全局事务隔离级别:set global transaction isolation level serializable

语法:set [session | global] transaction isolation level [read uncommitted  | read committed |  repeatable read |  serlializable];

毫无疑问,当前会话,往往用于开发或者测试人员在工作过程中,用于单元测试,而全局事务的更改往往用于实际的应用环境中。但是大多数据库一般使用了数据库默认的事务隔离级别。我们来演示一下,现在我们把当前会话的事务隔离级别更改为  未授权读取read uncommitted 。如下图所示,

新开一个会话,查询一下事务隔离级别,如下图所示,

现在把全局事务隔离级别设置为read uncommitted,如下图所示,

把刚才的另一个会话窗口关闭再打开,查询发现也已经是未授权读取的级别,这里要注意的是:在隔离级别修改之前的会话,如果不重新关闭再打开,在这个老的会话范围内依然还是原来的级别。

 

最佳实践

阿里巴巴中间件负责人毕玄,在早年发表的《分布式Java应用  基础与实践》一书中强调,实践是最好的成长

为了能够阐述的更加清楚一点,我们创建了一张表tb_user,同时两个字段id和name。下面就这张表作一下演示。演示过程中用到事务,我们就直接称为事务A、事务B、事务C....

①未授权读取。

我们把全局事务隔离级别置为未授权读取read uncommitted。

首先我们新开一个会话客户端A和B,同时,都开启事务,如下图示意,客户端B对原有的记录作更新操作,客户端A读取到了客户端B未提交的数据。

小结:未授权读取,能够读取到其他没有提交的的事务所操作的结果,即未授权读取不能避免脏读。

②授权读取。

我们把全局事务隔离级别置为未授权读取read committed。更改之后,当前会话并没有马上生效,需要重开一个窗口,也再一次证实了上面我提到的论点。

我们新开一个会话客户端A和B,同时,都开启事务,如下图示意,客户端B对tb_user先做更新,再做新增操作,在客户端B事务已经提交的情况下,客户端A读取到了客户端B新增的数据,和更新的数据。

小结:授权读取,只读取到其他事务已经提交的数据,能够避免脏读,但是不能避免可重复读取,上图客户端A中的事务在步骤2和7中的查询结果集不一致。

③可重复读取。

我们把全局事务隔离级别置为未授权读取repeatable read。

我们新开一个会话客户端A和B,同时,都开启事务,如下图示意,客户端B对tb_user先做更新,再做新增操作,在客户端B事务已经提交的情况下,客户端A没有取到了客户端B新增的数据,但是没有读取到更新的数据。

小结:可重复读,可以解决脏读和可重复读,但是对于幻读,可能还是会存在。在上面的实例中,新增,有可能是能读取到的,注意这里的用词是可能,可能各个数据库的版本有所不同。

 ④串行化。

串行化是最高级别的数据库事务隔离级别。

我们把全局事务隔离级别置为未授权读取serializable。如下图在客户端A开始事务,同时在客户端B也开启事务,在客户端B中执行select *语句,不带where条件,发现客户端A中的update语句进入等待的状态。

客户端A:

客户端B:

 

当客户端B执行commit;客户端A中的update语句立即执行。

小结:串行化的级别能够解决脏读,可重复读,幻读的问题。只要多个事务存在共享资源的竞争时,就会进入到一个等待的状态,当然两个事务都是对同一个表select查询操作是没有关系的。

注意:

只有在资源竞争时,才会串行化。不同的表,则不受干扰,同一个表的不同记录,也不受干扰。

1.客户端A开启事务执行select from tb_demo;客户端B开启事务执行update tb_user set name='123' where id=1;这两个事务互不干扰。

2.客户端A开启事务执行select * from tb_user where where id=1;客户端B开启事务执行update tb_user t set t.name='老王' where id=2;两个事务互不干扰。

原文地址:https://www.cnblogs.com/sunshine798798/p/9736506.html