oracle中有一个典型的问题是银行的转账业务
create table accounts(
account_number number primary key,
account_balance number
);
现在ACCOUNTS表格里的数据如下:
ACCOUNT_NUMBER ACCOUNT_BALANCE
-------------- ---------------
1 500
2 250
3 400
4 100
通过下面的语句查询帐户总额,
select sum(account_balance) from accounts;
现在假设当已经读取了第1行,准备读取第2行和第3行时,一台自动柜员机(ATM)针对这个表发生了一个事务,将$400.00从账户1转到了账户4,结果怎样?
在几乎所有的其他数据库中,如果想得到“一致”和“正确”的查询答案,就必须在计算总额时对整个表加上共享读锁,但这会大大影响并发性。
通过使用会滚段(Undo段),Oracle则不需要任何锁定,就可以得到正确答案。Oracle是这样做的:
view plaincopy to clipboardprint?<pre class="html" name="code">--------------------------------------------------------------
时间 查询 转账事务
--------------------------------------------------------------
T1 读第1行;到目前为止sum = $500
T2 更新第1行;对第1行加一个排他锁(也称独占锁,
exclusive lock),阻止其他更新。第1行现在有$100
T3 读第2行;到目前为止sum = $750
T4 读第3行;到目前为止sum = $1150
T5 更新第4行;对第4 行加一个排他锁,阻止其他更新(但不
阻止读操作)。第4行现在有$500
T6 读第4行,发现第4行已修改。这会
将块回滚到T1时刻的状态。查询从
这个块读到值$100
T7 提交事务
T8 得到答案sum = $1250
<pre class="html" name="code">--------------------------------------------------------------
时间 查询 转账事务
实际上oracle在执行查询的这个事务的过程中它是这样的。
因为这个事务它是查询的事务,所以它允许其他的事务去对这里的数据进行修改,但是如果这个事务本身是对正在访问的数据进行修改的话,那么它将不允许其他的事务对当前事务正在访问的数据进行修改,这个很好理解比如一个人在修改一张卡里面的金额,比如要取出来200,可是如果同时这个事务允许其他的事务同时执行的话,那么比如同时在其他的地方有人又向里面存了300,结果就是在第一个操作者看来取出钱了之后钱反而多了,这从逻辑上是肯定会造成误会的。
那么既然查询的业务它允许修改的进行那么这个事务有事如何保证他们的数据一致性呢?
oracle的版本控制在这个地方就有用了,同一个事务他们所访问的资源的版本是必须一致的,而这个版本就是以事务执行开始时的的时间为标准的,所以就上面的例子来讲,虽然在执行查询期间,卡之间的金额进行的转移,但是因为这个事务它的版本号是一定的,就是查询的起始时间是一定的,那么当oracle去取第4个卡里面的数据的时候它会按照本事务开始执行时所对应的时间去第4张卡里面取数据,因为这张卡的金额修改的事务它跟查询的事务的开始时间是不一样的,而oracle会去检查在那个时间段之后表里面的数据是不是被修改过,如果修改过那么它就会回滚事务到T1时刻,然后查询它的值,这样就保证了数据的一致性了。