时间会让我们更好。   

数据库事务隔离级别

哈哈,最近参加了一些课程,没来得及总结,更新慢了些。但是,还是一直记录下去的 ...

回到正题,这次介绍一下数据库事务中的隔离级别。

当数据库上有多个事务同时执行的时候,可能出现下面问题:

  • 脏读(dirty read):指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。通俗的讲,读到的数据是"脏"的。
  • 不可重复读(non-repeatable read):指指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。即不能读取到相同的数据内容。
  • 幻读(phantom read):指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行。就好象发生了幻觉一样。

为了解决上面这些问题,SQL标准对应有事务隔离级别的概念。SQL 标准的事务隔离级别包括:

  • 读未提交(read uncommitted):一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交(read committed):一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化(serializable):顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

举栗子说明:假设数据表中只有一列,其中一行的值为 1,下面是按照时间顺序执行两个事务的行为

在不同的隔离级别下,分析事务A下面V1、V2、V3 的返回值:

  • 读未提交:V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
  • 读提交:V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
  • 可重复读:V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
  • 串行化:在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。

在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准:

  • 读未提交:直接返回记录上的最新值,没有视图概念。
  • 读提交:视图是在每个 SQL 语句开始执行的时候创建的。
  • 可重复读:视图是在事务启动时创建的,整个事务存在期间都用这个视图。
  • 串行化:直接用加锁的方式来避免并行访问。

Oracle 数据库的默认隔离级别是“读提交”,因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,要记得将 MySQL 的隔离级别设置为“读提交”。

MySQL中配置事务隔离级别?

mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+

设置隔离级别:
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]