图1 Serial-Concurrent-Phenomenon示意图
2. 对账应用——对账不平
一个单位,有多位财会人员使用同一份数据对账,需要确保账结果是一样的,不能出现一个计算结果是5而另外一个是10的情况。
这个事情,在数据库中,对应事务的示例如:
如图2所示,两个分布式事务x和y并发执行,node1上局部事务s修改了数据项的值为a1后,子事务y读数据项的值为a1。而在node2节点上,与node1相似,局部事务t修改了数据项的值为b1后,只是子事务x读数据项的值为b1。所以事务x读取到的是(a0,b1)这样一个不一致状态的数据。同理,事务y读取到的是(a1,b0)这样一个不一致状态的数据
在实际应用中,账户对账业务,如果并发访问控制算法处理不当,则会发生这种数据异常。例如,事务x和y分别是两个对账人员同时进行对账,但是他们的对账结果却不同,这样的差异会令人费解。
图2 Cross-Phenomenon示意图
3. 电子交易——记账错误有一类数据异常,称为“Write Skew”。其表达式可简写为:
R1[x0] R2[y0] W1[y1] W2[x1]
Jim Gray在提出该异常时,给定的意义是:假设x和y满足某种完整性约束,如x y=0,那么修改之后的x和y,仍需满足该完整性约束。
在电子交易中,如银行的应用,有场景与此对应。
假设允许一个人的总账户下有两个子账户,每个子账户可各自进行存款和取款业务,但要求总账户上的存款余额必须大于等于零,即不允许透支。
如果使用该两个子账户取款,则存在透支风险。
假设两个子账户各自有存款x=10、y=20元,而各自取款30元(符合总账户不透支的要求),如果串行执行取款,则最多能取到30元,这是符合要求的;如果并发执行,则最多能取出60元,透支了30元,违反了完整性约束。因此该类数据异常需要在电子交易场景中消除。
4. 其他数据异常的现实意义如Dirty Read,如果读取了其他事务未提交的写操作的结果,采用该结果进行计算,则容易出现错误:错误发生在该“其他事务”回滚,其所写的结果就是一个不可能存在的结果,基于不可能存在的结果发生的读之后的计算则必然错误。如图3,是事务T2读取了一个事务T1临时生成的值,之后事务T1回滚致使该值在数据库中不存在,对于事务T2而言,会读到“未曾真实存在过的、临时的一种数据状态”因而是一种“异常”。该异常的特点,是事务T1的回滚操作,影响了T2曾经读到的数据;即回滚操作会对数据异常构成影响。
图3 脏读数据异常
Dirty Write未必会导致数据不一致,如交易类应用,记账时,分别给同一个元组的库存字段减去两次的交易量并都成功,则X-Y-Z和(X-Y)-Z的结果是一样的。但如果前一个写事务失败回滚,另外一个即需要分情况特殊处理才能确保最终结果符合A(原子性)和C(一致性)。
例如,两个事务(T1、T2)各自写了相同的数据项(row),T2事务覆盖了T1事务所写的值。但是,这么解释,有些牵强,因为存在一种情况是“W1[x0] C1 W2[x1]”事务T2的写操作同样覆盖了事务T1的写操作,这样算不算是脏写数据异常呢?很多数据库系统如PostgreSQL、Oracle等并不把“W1[x0] W2[x1]”作为脏写异常而处理,这是实践层面和ANSI-SQL标准脱节之处(也是和理论脱节之处)。
Non-repeatable Read/Phantom使得了本事务前后两次读到的数据不同,计算结果可能出现错误。如图4,是事务T2所写的值,被事务T1在T2写之前和写之后各自读了一次,但两次所读到的结果不同,结果不同导致“前后逻辑不一致”所以是一种“异常”。该异常的特点,表明两个并发事务的操作有交叉会构成数据异常。
图4 不可重复读数据异常
幻读数据异常,如图5所示,类似不可重复读异常,只是读取数据时,不是直接操作某个实体数据项,而是通过谓词进行读或写。该异常的特点,是读操作中带有了谓词,而谓词源自用户发出的SQL语句中的WHERE子句对应的条件,即谓词会对数据异常构成影响。但是,数据库界对于幻读异常认知不同,如下是ANSI/ISO-SQL标准(P3)和Jim Gray(A3)等对于幻读的定义。Jim Gray等定义认为前后两次相同的读操作,得到的结果集不同,第二个读操作的结果集因之前的写操作新加入数据项而被改变。从下面的形式化表达式中可知,A3比P3严格很多,P3是一种粗放式的定义,只要发生带谓词的读以及之后发生一个其他事务的符合谓词范围的写操作,则发生幻读,这是一种扩大了防范范围的定义方式,如果后面不发生第一个事务带谓词的读操作,实际上不会发生幻读,所以A3的定义方式更为严谨一些。但是,A3中的提交操作即c2是必须的吗?这个问题没有被深入研究而讨论过,这表明A3的定义其实并不严谨。换句话说,有必要深入研究,数据异常究竟应该怎么严谨地定义?
P3:r1[P]...w2[y in P]...(c1 or a1) (ANSI/ISO-SQL标准定义的Phantom)
A3:r1[P]...w2[y in P]...c2...r1[P]...c1 (Jim Gray等定义的Phantom)