【MySQL】-事务的隔离级别

来源:互联网 发布:手机网络延迟高怎么办 编辑:程序博客网 时间:2024/05/21 11:16

概述

在标准的SQL中事务的隔离级别一共有四种。事务的隔离级别主要定义了一个事务做出的修改在别的事务中的可见性情况。事务的隔离级别越高通常开销越低,同时并发程度也越高。事务的隔离级别在不同的存储引擎中的实现也是不尽相同的,这里只简单介绍四种标准的隔离级别。(本文很多摘抄自《MySQL》第三版)

四种标准的隔离级别分别为:READ UNCOMMITTED(未提交读),READ COMMITTED(提交读),REPEATABLE READ(可重复读),SERIALIZABLE(可串行化)。

READ UNCOMMITTED(未提交读):
在一个事务中的修改,即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据----脏读(Dirty Read),这个级别会导致很多的问题,并且从性能上讲也不会比其他的级别好很多,同时也缺少其他级别的很多好处,因此一般都不推荐使用。

READ COMMITTED(提交读):
一个事务开始时只能看到已经提交的事务所做的修改。也就是说一个事务从开始一直到提交之前所做的任何修改对其他事务都是不可见的。这个级别有时候又叫做不可重复读。大多数的系统的默认的隔离级别都是READ COMMITTED。但是这个级别不能保证在同一个事物中多次读取相同的记录的结果的一致(因为可能有别的事务对这些数据做出了修改并提交了)。

REPEATABLE READ(可重复读):
REPEATABLE READ(可重复读)在解决了脏读的基础上保证同一个事务中多次读取相同的记录的结果是一致的。但是在理论上还存在一个“幻读”(Phanpom)的问题。也就是在读取某部分的记录时候有别的事务在这部分记录的范围内插入了新的纪录,那么重复读的时候就会出现一个新的纪录。不过InnoDB和XtraDB的存储引擎通过多版本控制(MVCC, Mutivertion Concurrency Control)解决了“幻读”的问题。MySQL中默认使用的就是REPEATABLE READ级别。

SERIALIZABLE(可串行化):
强制事务串行的执行。从而避免了“幻读”问题。它会在读取记录的时候对每一行都加上锁,这样就导致了大量的锁竞争的问题。所以一般也很少使用。


“脏读”和“幻读”:“脏读”针对的是update,delete语句,或者说针对的是修改产生的数据。核心问题还是锁的问题,因为update和delete前提都是数据行存在,所以一个事务能否修改记录就取决与能否获取到该记录的锁。而“幻读”针对的是insert操作,因为数据行不存在所以当一个事务重复读取的时候可能看到别的事务插入的记录。


表:


*其中MySQL中默认的存储引擎InnoDB使用的REPEATABLE READ级别则通过MVCC解决了“幻读”的问题。


MySQL修改事务隔离级别

前面说了MySQL默认的事务隔离级别是REPEATABLE READ但是大多数的数据库的默认隔离级别都是READ COMMITTED,在MySQL中提供两种方式修改事务的隔离级别

配置文件

在mysql,ini配置文件中加入

[mysqld]transaction-isolation = READ-UNCOMMITTED
可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE

客户端设置

在客户端可以用下面的语句查询
mysql> select @@global.tx_isolation,@@tx_isolation;+-----------------------+-----------------+| @@global.tx_isolation | @@tx_isolation  |+-----------------------+-----------------+| REPEATABLE-READ       | REPEATABLE-READ |+-----------------------+-----------------+1 row in set (0.00 sec)

其中@@global.tx_isolation表示全局的事务隔离级别,而tx_isolation表示当前会话的隔离级别。


设置隔离级别:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
例:
mysql> set session transaction isolation level read committed;Query OK, 0 rows affected (0.00 sec)
再查询发现已经设置成功了:
mysql> select @@global.tx_isolation,@@tx_isolation;+-----------------------+----------------+| @@global.tx_isolation | @@tx_isolation |+-----------------------+----------------+| REPEATABLE-READ       | READ-COMMITTED |+-----------------------+----------------+1 row in set (0.00 sec)

设置全局的事物隔离级别:
mysql> set global transaction isolation level read committed;Query OK, 0 rows affected (0.00 sec)
这个时候再查询:
mysql> select @@global.tx_isolation,@@tx_isolation;+-----------------------+----------------+| @@global.tx_isolation | @@tx_isolation |+-----------------------+----------------+| READ-COMMITTED        | READ-COMMITTED |+-----------------------+----------------+1 row in set (0.00 sec)
同时我在另一个连接中查询发现全局事务隔离级别已经被更改:
mysql> select @@global.tx_isolation, @@tx_isolation;+-----------------------+-----------------+| @@global.tx_isolation | @@tx_isolation  |+-----------------------+-----------------+| READ-COMMITTED        | REPEATABLE-READ |+-----------------------+-----------------+1 row in set (0.00 sec)

细心的人可能就会发现明明全局事务隔离级别都修改了为什么另外一个连接的会话级别的事务隔离级别没有变化。
这里需要说明的是:当使用set修改了全局的事务隔离级别时候只对设置以后的连接的默认事务隔离级别有影响。

这个时候我再打开一个新的连接就会发现默认的事务隔离级别都是READ COMMITTED了。

对于这四个事务隔离级别的实例测试其实很简单,只需要分别开两个连接,同时开启两个事务(不着急commit)然后根据case测试就很容易得出结果了。这里就不一个一个贴出来了。