参考《数据库事务处理的艺术》和《分布式数据库原理、架构与实践》,以及https://www.gatevidyalay.com/tag/serializability-and-recoverability-in-dbms/,本文有修改。
一、九大异常
首先给出事务处理中目前研究出来的九大异常,然后指出何种事务隔离级别可以避免该种异常。
1. 读异常
1.1 脏读
事务T1写了某个数据,尚未提交,T2就读取了该数据,称为脏读。由于未提交的数据写很有可能abort,也有可能再次修改,会产生数据一致性问题。
【READ COMMITTED 事务隔离级别可避免这一现象】
READ COMMITTED 一般也是RDBMS的默认隔离性级别,原因在于脏读可能导致事物的可恢复性受影响。比如事务T1写了该数据最后回滚,T2读了该数据,经过一系列逻辑提交。由于T1的回滚有可能造成T2提交的状态是不合法的,因此事务T1就不具备可恢复性,没法回滚。
1.2 不可重复读
事务T1在两次读某一行数据之间某时刻,事务T2对该数据修改,并已提交,那么事务T1两次读取的值是不一致的。
【REPEATABLE READ 事务隔离级别可避免这一现象】
1.3 幻读
事务T1读取某一批值,带有筛选条件的(即,有where子句)。之后事务T2在表中插入和删了一些数据并提交。此时T1再去用同样的筛选条件读取数据,发现和上一次不一致,尽管T1什么都没做,只是连续读了两次。
【SERIALIZABLE 事务隔离级别可避免这一现象】
幻读是最难以处理的数据异常,因为不仅要处理已有的数据,还要预防未来的数据。
2. 写异常
2.1 脏写
类似脏读,事务T1写了某行数据,事务T2又去写了这个数据,然后提交了。由于T1之后可能abort,这就会回滚T2写的数据,产生一致性问题。
【一般任何隔离性级别都不会允许脏写】
2.2 更新丢失
和脏写情况很像,事务T1可能最后没有abort,但无论如何T1最开始对数据的修改已经丢失了,被T2覆盖掉了。
【REPEADTABLE READ 级别即可避免】
3. 不可串行化异常
不可串行化异常是指这样的调度是不满足冲突可串行化的,类似的调度优先图是有环的。
3.1 写倾斜
考虑事务T1和T2在开始都读了一个数据x,然后T1以这个值作为条件(x>1)作了其他事(比如x=x-1),紧接着T2也以这个值作为条件(x>1)作了其他事(比如x=x-1)。那么此时x=0,就有可能是一个不允许的状态。而且这两个事务也是不可串行化的(可串行化执行的话x肯定大于0)。
三个事务也可能出现类似的事情,其本质就是不可串行化。
【REPEADTABLE READ 级别即可避免】
3.2 读倾斜
事务T1先读了x,事务T2修改了x,y并提交,然后T1读了y,发现x和y不满足一致性关系。本质也是不可串行化。
【REPEADTABLE READ 级别即可避免】
二、事务一致性和隔离性
一致性是ACID里的C,隔离性是I。这两个特性的定义一致比较模糊,而且概念高度相关(可以理解为:一致性是隔离性的结果,不同的隔离级能达到不同的一致性,或者说造成不同级别的数据异常。而事务隔离是实现一致性的手段)。
- 比如,一致性的定义是事务只会把数据库从一个合法状态到另一个合法状态。可是什么是“合法状态”呢?这个实际上只能取决于用户的业务逻辑和数据语义,已然超出了数据库的基本功能范畴。
- 另一方面,从系统的角度,一致性还需要包括可串行性和可恢复性。
- 可恢复性的概念:提交的事务没有读取abort的事务写的数据。因为abort之后,写的数据被回滚,那这个提交的事务做的其他修改就会造成不一致。实际上就是不能脏读。
- 可恢复性还有两个更严格的概念,一个是避免级联回滚,一个是严格性。
- 避免级联回滚:也是脏读,然后abort产生的级联回滚,避免级联回滚只需要避免脏读。
- 严格性:先写某个数据的事务必须先提交。保证严格性,只需要可重复读的隔离级别。
- 隔离性:一般定义为多个事务并发执行时互相透明。实际上要想实现隔离性,就需要最高的隔离级别:可串行性SERIALIZABLE。我们常说的隔离级别是说在各种级别上牺牲隔离性,换取并发量。
三、调度可串行性和可串行(SERIALIZABLE)隔离级别
这两个是完全不同的概念,虽然名字一样,但实际含义相差甚远。
- 我们首先说SERIEALIZABLE隔离级别,这个级别可以解决我们上述的九大问题中的任何问题,也可以保证严格性(自然也保证避免级联回滚和可恢复性)。其具体的实现有很多种(通常是基于锁来实现),但总之设置为这个级别基本是一个万金油,不必考虑任何一致性问题,但显然并发量也是最低的。
- 接下来是调度的可串行化,这是一个课本概念,描述的是通过交换不冲突的指令,一个调度可以等同于一组事务按照某种串行的顺序完成。通常称之为冲突可串行化。但是注意,这里的指令是不包括abort的(一般也包括commit)。
- 一个调度可串行化,是允许脏读的。脏读的情况不会在优先图中产生环。这意味着可串行化属性不表示调度是可恢复的。
- 如果一个调度可串行化,不可重复读是不会出现的,不可串行化异常也不会出现,而幻读和两种写异常是有可能出现的。
- 【编者认为,REPEATABLE READ这一隔离级别,就足以保证调度是可串行化的了】
- SERIALIZABLE隔离级别足以保证调度的可串行性;相反,仅仅是调度的可串行性,是远远不足以达到SERIALIZABLE隔离级别的要求的。这是因为事务还有原子性的要求,如果没有原子性的要求,也就不需要commit abort之类的指令,可串行性也就足够支持事务一致性了。
-
实际上,由于原子性的存在,事务一致性的保证不仅需要可串行性,还需要可恢复性,在abort时不会产生额外的数据不一致。
image.png
网友评论