我由幻读而明白了数据库事务

344 阅读4分钟

我由幻读而明白了数据库事务

刚开始接触数据库事务的时候,总会被脏读,不可重复读,幻读,读未提交,读已提交,可重复读等等这些“读”名词整的特别混乱。只能去硬记数据库事务的级别,以及每个级别可以解决的问题,和不同数据库实现的事务级别,直到后来彻底看完innodb引擎对事务的处理,才算彻底理清这些事务之间的关系,当别人再问起来时,可以马上根据底层原理说清楚与理论的关系。

本文中还是以innodb引擎为例来进行说明,其他数据库或引擎所知甚少就不谈及了。

简单的说事务就是保证一组操作要么全部成功要么全部失败,提到事务就会说起事务特性ACID(原子性,一致性,隔离性,持久性),这里我们主要说事务的隔离性。

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

还有下面这个看似简单实则迷惑的表格

隔离级别脏读不可重复读幻读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重复读),可以通过命令来查询或者修改事务级别。

我们可以根据每个级别来分析,最低的RU级别其实都不需要做处理,脏数据或重复数据都会出现,而在RC与RR级别下,innodb通过undo log来实现多版本并发控制(MVCC)中用到的一致性读视图。

1. RC事务级别中是允许读取到已经提交的数据,包括修改和增加数据,这也是大多数数据库支持的事务隔离级别,因为读到都是其他事务提交的,一般来说是可以接受的,并不会带来很大的问题,但其违反了事务一致性的规定。在RC事务隔离级别下,仅采用Record lock,除了唯一性的约束检查与外键约束的检查需要gap lock,其他地方不使用gap lock的锁。

2. 在RR可重复读的事务级别下,innodb使用Next-key lock锁的算法来避免不可重复读的问题,即幻象问题,在Next-key lock锁下,对于索引的扫描不仅是锁住扫描到的索引,而且还锁住这些索引覆盖的范围(一个前开后闭的区间,包括记录本身),在这个范围内插入都是不允许的,避免了其他事务在这个范围内插入数据而导致不可重复读的问题。

3. 在SERIALIABLE的事务隔离级别,innodb引擎会对每个select语句后自动加上LOCK IN SHARE MODE,为每个读取操作加上一个共享锁,因为在读占用了锁,所以不再支持一致性读非锁定读。innodb在RR隔离级别下就可以到达SQL标准的SERIALIABLE级别,因此一般不在本地事务中使用SERIALIABLE的隔离级别。

综上,innodb更加完美的解决了幻象问题,在默认情况下设置为REPEATABLE-READ的隔离级别即可,事务级别越低涉及到的锁越少,虽然REPEATABLE-READ级别会将Record Lock升级为Next-key lock锁,但并不会比READ-COMMITTED级别有性能损耗。