数据库事务及隔离级别

141 阅读6分钟

事务是什么?

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。

事务的四个特性是什么?

事务的四个特性包括:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolcation)和持久性(Durability),简称ACID。

事务的隔离级别是什么?

当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

什么是脏读、不可重复读和幻读?

  • 脏读(读取未提交数据),比如,A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。

  • 不可重复读(前后多次读取,数据内容不一致),比如,事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如,此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

  • 幻读(前后多次读取,数据总量不一致),比如事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

事务的隔离级别有哪些?

SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和可串行化(serializable )

  • 读未提交,是指一个事务还没提交时,它做的变更就能被别的事务看到;最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读;

  • 读提交,是指一个事务提交之后,它做的变更才会被其他事务看到;允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生;

  • 可重复读,是指一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的;当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的;对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生;

  • 可串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;可串行化是最高的隔离级别,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

隔离级别脏读不可重复读幻影读
read uncommited
read commited×
repeatable read××
serializable×××

能否举例说明四个隔离级别的区别?

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

事务A事务B
启动事务,查询得到值1启动事务
查询得到值1
将1改成2
查询得到值V1
提交事务B
查询得到值V2
提交事务A
查询得到值V3

在不同的隔离级别下,事务 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 语句开始执行的时候创建的。这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

什么时候需要“可重复读”的场景呢?

数据校对逻辑的案例。假设你在管理一个个人银行账户表。一个表存了账户余额,一个表存了账单明细。到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。

有关的

  • 在MySQL中,事务支持是在引擎层实现的。

  • MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一。

  • Oracle 数据库的默认隔离级别其实就是“读提交”;MySQL InnoDB 存储引擎的默认支持的隔离级别是“可重复读”。