数据的一致性
事务(Transaction)是并发控制的基本单位。所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的工作单位。例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行,所以,应该把他们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
[@more@]事务有如下特性:
原子性(A):在事务内的所有操作要么一起成功,要么一起失败;
一致性(C):当事务失败退出时,所有在本事务中被修改的数据,会被恢复到事务开始时的状态;
独立性(I):在事务执行过程中,已被修改但未被提交的数据,对于其它事务是不可见的;
永久性(D):已被提交的数据是永久的,不可再恢复的。要取消已提交的事务,必须在应用级实现,例如再进行一次相反的事务实现反冲。
可以看到,在不同的应用场合,事务实现的复杂度是不同的。
1、单用户单数据源的事务只需保证事务内的操作一致就可以了,即在事务内的所有操作要么一起成功,要么一起失败。在这种应用环境,只要有一个良好的日志机制,就可以保证事务的完整性。
2、多用户单数据源在此基础上还需保证并发环境下的事务间数据一致性问题。在这种应用环境,必须考虑事务之间的隔离。
多用户多数据源在以上两者基础上还需实现不同数据源之间的同步。 在这种应用环境,如何在提交时同步各数据源就是一个非常重要的问题,在此,必须引入两阶段提交这个概念(本文暂不讨论两阶段提交)。
单一数据源事务的数据一致性
在单一数据源环境下,有多个用户存取和更改数据时,必须保持数据源中数据的完整性。 在此,必须考虑并行性问题,即可以同时由多个交互的用户或应用程序共享资源的能力。
并发事务中的一致性问题
在并发事务环境中,除了必须满足单用户单数据源的事务的特性,还有并发环境下特有的数据一致性问题。
1---丢失更新(Lost Update):两个应用程序A和B,可能同时从数 据库中读取相同的行并同时根据这些应用程序读取的数据为其中一列计算新的值。
如果A用它的新值更新该行而B随后也更新该行,则由A执行的更新将丢失。
在下表中,T1、T2、T3和T4表示顺序的时间。
用户 | T1 | T2 | T3 | T4 |
A | X=40 | X=x-30 | ||
B | x=40 | X=x-20 |
假设用户A和B都读取x(x=40),然后分别把x减少30和20。用户A在t3把改后的x(x=10)写入数据库。随后,用户B在t4把改后的x(x=20)写入数据库。于是,对用户A而言,他的修改在t4处丢失了。
2---存取未提交的(脏)数据(Dirty Read):应用程序A可能更新数据 库中的值,而应用程序B在该值被提交前可能会读取它。
因此,如果A的值稍后未被提交,而是被收回,则由B执行的计算将基于 未提交的(可能为无效的)数据。
用户 | T1 | T2 | T3 | T4 |
A | x=40 | X=x+30 | rollback | |
B | X=70 |
用户A在t2把x增加30(尚没写入数据库),用户B在t3由数据缓存读出x=70。但用户A在t4时撤消(Undo)了对x的修改,数据库中仍维持x=40。但用户B已把“脏”数据(x=70)取走。
3---不可重复读:(Non-Repeatable Read):某些应用程序涉及到下列一系列事件:应用程序A从数据库中读取一行, 然后继续处理其他SQL请求。
与此同时,应用程序B修改或删除该行并提交更改。稍后,如果应用程序A试图再次读取原始行,则它将接收到修改的行或发现该原始行已被删除。
用户 | T1 | T2 | T3 | T4 | T5 | T6 |
A | X=40 | Y=30 X+Y=70 | Z=30 X+Y+Z=100 | |||
B | x=40 | X=X+20 | COMMIT | X=x-20 |
用户A、用户B分别读取x=40后,在t3用户A取出y=30并计算x+y=70。在t4时用户B把x增加20,并于t5把x(x=60)写入数据库。在t6时,用户A取出z(z=30)并继续计算x+y+z=100。但如果用户A为进行核算而把x,y,z重读一次再进行计算,却出现x+y+z=120!(x已增加20)。
4---幻象读现象(Phantom Read): 在下列情况下将出现幻象读现象:
1.应用程序执行一个查询,该查询基于某些查询条件读取一组行。
2.另一个应用程序插入新数据或更新现有数据而了满足查询条件。
3.您的应用程序从步骤1开始重复查询(在同一事务中)。
当重复查询(步骤3)时,某些附加的(“幻象”)行作为结 果集的一部分返回,这些行在初始执行查询(步骤1)时不会被返回。
考虑如下情况,
C1 | C2 |
1 | ABC |
2 | BCD |
3 | CDE |
执行如下操作
用户 | T1 | T2 | T3 | T4 |
A | Select * from Tab1 where C1 <=2 | Select * from Tab1 where C1 <=2 | ||
B | Insert into Tab1 values(0,'XXX') | COMMIT |
用户B在用户A执行第一次选择操作后插入一条记录并提交,使得用户A在第二次执行此查询操作时结果集比第一次多了(0,'XXX')
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7437037/viewspace-912545/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/7437037/viewspace-912545/