第三代分布式数据库(4)——为什么要100%保证数据的正确性?(下)
续接上一篇:《第三代分布式数据库(4)——为什么要100%保证数据的正确性?(上)》
数据库教科书,通常会抽象地、不完备地、且非数学方式不严谨地讲四个数据异常(ANSI-SQL标准定义的Dirty Write、Dirty Read、Non-repeatable Read、Phantom),读后会很容易“不理解”!
其实,结合实际的应用背景,可以很容易地理解数据异常。
1. 对账应用——对账错误
金融交易中,对账是一个重要的工作,通过对账,可知道“总账目应当是平衡的”。
这个事情,在数据库中,对应事务的示例如:
如图1所示(分布式环境下,一个被称为Serial-Concurrent-Phenomenon的异常),写事务正在执行从Na节点的X账户转账10元到Nb节点Y账户。当Na节点完成提交,而Nb节点尚未提交,此时,一个读事务的读操作,从Na节点读取到的是新值“X-10”,而从Bb节点读取到的是旧值“Y”,对账写事务之前的“X+Y”与读事务读到的“X-10+Y”,账户总账不平。
如果数据库系统的实现存在问题,单机系统下,该异常也能出现。
该异常本质上是“Write Skew Committed”类异常。
图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)
图5 幻读数据异常
Lost Update使得用一个合法的更新过的值不存在而不能被读取到,从而丢失了正常的正确的记账结果。
Write Skew互相破坏了对方数据在读取数据前保持的一致性,因此用两个互不一致的状态参与计算,致使计算结果有误。
其实,不只是有这些异常现象,在论文《数据库管理系统中数据异常体系化定义与分类》(本文的内容基于该文,请先阅读该论文)中系统总结了各种不同一致的数据异常现象,读者可详细参考。
继ANSI/ISO-SQL标准定义了四种数据异常和Jim Gray定义了八种数据异常之后,Andy通过定义隔离级别也对数据异常进行定义(采用两种方式,一是基于冲突图定义环来定义数据异常,二是把几个“不能纳入环方式定义数据异常”的几个情况作为特例加以定义)。
之后,如表2所示,从二十世纪的九十年代到二十一世纪的二十年代,三十多年间,不同的参考文献分别零星的发现、定义了十几个不同的数据异常,这使得数据异常的数目发生了变化,进一步扩展了人类对于数据异常的认知。众多的数据异常虽然被不断报告,但是没有引发人类对于数据异常的系统化思考,比如,存在的问题有:究竟有多少个数据异常?十几个已知的数据异常之间有什么关系?这十几个数据异常怎么对应到ANSI/ISO-SQL标准定义的四个隔离级别下?同理,这十几个数据异常怎么对应到Jim Gray定义的六个隔离级别下?数据异常究竟有什么价值?
再之后,进入二十一世纪,参考文献《数据库管理系统中数据异常体系化定义与分类》系统地对数据异常进行梳理和思考,通过形式化的方法,尝试定义所有的数据异常,并试图研究数据异常的本质和对事务处理技术的影响,这使得对数据异常的研究进入一个新的阶段。
更进一步信息可参考文献《数据库管理系统中数据异常体系化定义与分类》(http://www.jos.org.cn/jos/article/pdf/6442)或者《硬核干货 | 数据异常的本质和价值详解》(https://www.modb.pro/db/168385)。
请思考几个问题:
1. 您能说出多少个数据异常?教科书能说出多少个数据异常?
2. 有谁能说出哪些数据异常在哪些场景中会出现?从而能指导你在应用开发中去避免数据不一致性现象?
3. 有谁能说清楚数据异常和隔离级别的关系?
4. 有谁能说出数据异常和数据库系统的性能的关系?
如果您不能回答这些问题,请问数据异常是否为您的基于数据库的应用开发带来了心智负担?是否您会担心数据的不一致性现象?
如果我们不想迷失在“一致性的迷雾”中,如果我们想清晰地认知数据正确性,如果我们不想基于数据库的应用存在任何心理负担(担心数据不一致),如果我们想在数据正确性方面永远高枕无忧,那么,100%确保数据正确性,是非常必要的。
另外,在100%保证数据正确性的同时,可得到最大化的并发性能。鱼和熊掌可兼得。这一点,是可证明的。
换句话说,隔离级别是无用的,只保留可串行化隔离级别即可(未来进一步探讨可串行化隔离级别存在的问题)。
前面,我们从宏观层面聊了聊分布式数据库与理论相关的内容。接下来,我们将进入实践部分。
每一个有技术尊严的数据库产品,必然会特别重视数据库所提供的核心能力——数据正确性——的实现。
基于MySQL做应用开发的人群基数非常庞大,MySQL的开发者非常想在数据的正确性方面“避坑”,作为MySQL的关注者,你知道在MySQL的数据正确性方面,如何“避坑”?MySQL的数据正确性是如何保证的吗?
下一篇,首先谈谈MySQL:
1. MySQL的事务处理技术,+深度洞察(究竟有什么样的深度洞察呢?这些洞察,是每一个事务技术层面的内核开发者必知必会必懂必学的知识)。
2. 您是否知道:MySQL的数据正确性在细节方面是如何保证的?效果怎么样?--读后可思考,为什么教科书从来没有从本文给出角度来理解事务处理技术呢?
3. 您是否知道:在理论指导下,如何从编程角度最大化并发性能吗?
下一篇,我们将谈的是不一样的、未曾被讨论过的、让人脑洞大开的MySQL的事务处理技术+重要细节。读后,您也许会豁然大悟,“原来事务处理技术竟然可以这么简单地理解”!其中涉及大量的技术细节,但不流于技术细节,而是高屋建瓴地结合理论和实践进行高度总结,丰富实例进行精细分析,以期一目了然一举掌握MySQL的事务处理技术。
另外,下一节我们也将更多涉及隔离级别和数据异常的关系。
[1] Andrea Cerone, Alexey Gotsman, and Hongseok Yang. Algebraic laws for weak consistency. International Conference on Concurrency Theory (CONCUR 2017), LIPICS 85, pages 26:1-26:18, 2017.
[2] Lamport L, Shostak R, Pease M. The Byzantine generals problem[J]. ACM Transactions on Programming Languages and Systems (TOPLAS), 1982,4(3):382-401
[3] P. Bailis, A. Fekete, J. M. Hellerstein, A. Ghodsi, and I. Stoica, “Scalable atomic visibility with ramp transactions,” in SIGMOD, 2014, pp. 27–38.
[4] Hal Berenson, Philip A. Bernstein, Jim Gray, Jim Melton, Elizabeth J. O'Neil, Patrick E. O'Neil:A Critique of ANSI/ISO-SQL Isolation Levels. SIGMOD Conference 1995:1-10
[5] 李海翔,李晓燕,刘畅,杜小勇,卢卫,潘安群.数据库管理系统中数据异常体系化定义与分类.软件学报,2022,33(3):0
[6] A. Adya, B. Liskov, and P. O’Neil. Generalized isolation level definitions. In Proceedings of the 16th International Conference on Data Engineering, ICDE ’00, pages 67–78, Washington, DC, USA, 2000. IEEE Computer Society.
[7] ANSI X3.135-1992, American National Standard for Information Systems – Database Language – SQL, Nov 1992.