丢失更新

阅读更多

                                                                 丢失更新
丢失更新(lost update)是一个经典的数据库问题。实际上,所有多用户计算机环境都存在这个问题。
简单地说,出现下面的情况时(按以下所列的顺序),就会发生丢失更新。
(1)会话session1的一个事务获取(查询)一行数据,放入本地内存,显示给用户User1
(2)会话session2另一个事务也获取这一行,但显示给User2
(3)User1使用应用修改了这个一行并提交。session1的事务已经执行
(4)User2也修改这行并提交。session2的事务也执行。
这个过程称为“丢失更新”,因为第(3)步所做的所有修改都会丢失。
解决这个问题,Oracle有2种解决方案
1)悲观锁定
 用户在屏幕上修改值之前,这个锁定方法就要起作用。例如,用户一旦有意对他选择的某个特定的行(屏幕可见)
执行更新,如单击一个按钮,就会放上一个行锁。那这个行锁就会持续到用户修改并提交。
select empno,ename,sal from emp where deptno = 10;
EMPNO ENAME SAL
----- ----- ----
7782 CLARK 2450
7839 KING 5000
7934 MILLER 1300
用户想更新MILLER行。在这个时间点上(即用户还没有在屏幕做任何修改,但这行已经从数据库中读出一段时间了),
应用会绑定用户选择的值,从而查询数据库,并确保数据尚未修改。可以使用一下命令模拟
variable empno number
variable ename varchar2(20)
variable sal number
exec :empno := 7934; :ename := 'MILLER'; :sal := 1300;
下面,除了简单查询值并验证数据尚未修改外,我们用使用FOR UPDATE NOWAIT 锁定这一行。
SELECT EMPNO,ENAME,SAL FROM EMP WHERE EMPNO = :EMPNO AND DECODE(ENAME,:ENAME,1) = 1
AND DECODE(SAL,:SAL,1) = 1 FOR UPDATE NOWAIT.
从而不允许其他会话更新。因此,这种方法称为悲观锁定(pessimistic locking)。
 如果底层数据没有改变,就会再次得到MILLER行,而且这一行会被锁定,不允许其他会话更新(但是允许其他会话读)。
 如果另一个用户正在更新这一行,我们就会得到一个ORA-00054:resource busy错误。相应地,必须等待更新这一行
的用户执行工作。
 在选择数据和指定有意更新之间,如果有人已经修改这一行,我们就会得到0行。这说明,屏幕上的数据是过时的。为了避免
前面所述的丢失更新情况,应用需要重新查询,并在允许在最终用户修改之前锁定数据。
 一旦成功地锁定了这一行,应用就会绑定新值,发出命令后执行修改:
 UPDATE EMP SET ENAME = :ENAME,SAL = :SAL WHERE EMPNO = :EMPNO; 
2)乐观锁定
 第二种方法称为乐观锁定,即把所有锁定都延迟到即将执行更新之前才做。换句话说,我们会修改屏幕上的信息而不要锁。
我们很乐观,认为数据不会被其他用户修改。因此,等到最后一刻才知道我们的想法正确与否。
 乐观控制的方法有很多种,主要介绍2种方法:
 1、使用一个特殊的列,并且这个列由数据库的一个触发器或者应用程序代码来维护,可以告诉我们记录存储现行的版本号。
 2、通过原来的数据计算校验和或散列值。

乐观锁定还是悲观锁定
 那么哪种方法最好呢?根据我的经验,悲观锁定在Oracle中工作得非常好(但在ita数据库当中可能不是这样),而且与乐观
锁定相比,悲观锁定有很多优点。不过,它需要与数据库有一条有状态的连接,如客户/服务器连接,因为无法跨越连接持有锁。
所以在当前的许多情况下,悲观锁定不太现实。过去的应用可能只有数十个或上百个用户,对于这些应用,悲观锁定是我们的不二
选择。不过对现今大多数应用来说,一般都采用乐观并发控制。
 在这些可用的方法中,我使用哪一种呢?推荐使用版本列方法,并增加一个时间戳列。从长远看,这样能为我们提供一个额外
的信息。而且与散列和检验和方法相比,计算的代价不是很昂贵,在处理LONG,LONG RAW,CLOB,BLOB和其他非常大的列时,散列或
检验和方法可能会遇到一些问题,而版本列方法则没有这些问题。

你可能感兴趣的:(丢失更新)