为了避免丢失更新(更新冲突),要使用某种锁定策略,共有两种锁定策略:悲观锁定或乐观锁定。
一、悲观锁定(pessimistic locking):
1.表现:用户在屏幕上修改值之前,这个锁定方法就要起作用。例如,用户一旦有意对他选择的某个特定行(屏幕上可见)执行更新,如单击屏幕上的一个按钮,就会放上一个锁。
(悲观的认定每次数据存取,其他客户端也会同时存取这一数据,因而马上锁定,直到当前操作完成)
2.悲观锁定仅用于有状态(stateful)或有连接(connected)的环境,这是20世纪90年代中期客户/服务器应用中的一种流行做法。但现在采用有状态方式的连接方法已经不太常见了(不过并没有完全消失),特别是随着20世纪90年代中后期应用服务器的出现,有状态连接更是少见。
悲观锁其实就是 传统锁,就是锁了别人进不来,无法更新,无法读取,而hang out,等time out后抛出异常。
3.例子:
select empno, ename, sal
from emp
where empno = :empno
and ename = :aname
and sal = :sal
for update nowait
二、乐观锁定(optimistic locking)
即把所有锁定都延迟到即将执行更新之前才做。
表现:在修改屏幕上的信息而不要锁。我们很乐观,认为数据不会被其他用户修改;因此,会等到最后一刻才会去看我们的想法对不对。
这种锁定方法在所有环境下都行得通,但是执行更新的用户“失败”的可能性会增大。这个用户要更新它的数据行时,发现数据已经修改过,所以他必须重头再来。
乐观锁其实 并不是一种 锁, 只不过是一种策略,比如我们平时用的sub version就是采用其中的一种最常用的策略: versionstrategy: 就是在要锁的表的 数据库记录 加一个 version控制。(原则是 the older versions' data is restricted from overwriting any data of the newer versions. )
本质上是一种检测手段,不管你如何修改,最后由软件来实现merge(合并)
可以在应用中同时保留旧值和新值,然后在更新数据库时使用如下的更新语句,这是乐观锁定的一种流行实现:
updtae table
set column1 = :new_column1, column2 = :new_column2,...
where primary_key = :primary_key
and column1 = :ld_column1
and column2 = :ld_column2
...
这种情况下,可能很幸运地更新了一行,但如果另一个人已经修改了数据,就会失败。现在须确定下一步要做什么,是让最终用户重新查询这一行现在的新值,然后再重新开始新事物呢(但是这一行有可能又被修改了,这可能会使用户很受打击)?还是根据业务规则更新冲突,试图合并两个更新的值(这需要大量的代码)?
乐观锁的实现:大多基于数据版本记录机制,一般是在数据库表中加入一个version字段,读取数据时将版本号一同读出,之后更新数据时版本号就加一,如果提交数据时版本号小于或等于数据表中的,则认为数据是过期的,否则才给予更新。version控制支持lazy
缺点是解决用户输入大量数据结果很容易发生无法更新
乐观锁还可以使用世间截等方式,本质上都是依据 某 判断是否可以覆盖当前数据库中的数据。
另外,上面的update有可能被阻塞。
三、比较
如果所有应用(会话)都使用乐观锁定,执行更新并提交时,行只会被锁定很短的时间。但如果某些应用使用了悲观锁定,它会在较长时间内持有行上的锁,你可能就会考虑使用select for update nowait,以此来验证行是否被修改,并在即将update之前锁定来避免被另一个会话阻塞。
四、另外三种实现乐观并发控制的方法:
1、使用版本列的乐观锁定。
对每个要保护的表增加一列,这一列通常是number或date/timestamp列,通常通过行触发器来维护。
在应用中只需保存这个版本列的值,而不用保存所有其他列的值。
为了维护这个版本列,建议把更新逻辑封装到一个存储过程中。另一个实现方法是使用触发器,但是触发器会引入大量开销。
2、使用校验和的乐观锁定
这与前面的版本列方法很相似,在此要使用计数据本身来计算一个“虚拟的”版本列。有很多方法计算散列或校验和。以下是其中的三种:
(1)OWA_OPT_LOCK.CHECKSUM Oracle8.1.5)及以上版本中提供,出现冲突的可能性是1/65536。
(2)DBMS_DBFUSCATION_TOOLKIT.MD5 Oracle8.1.7)及以上版本中提供,出现冲突的可能性是1/(3.4E+38),非常小。
(3)DBMS_CRYPTO.HSAH Oracle10.1)及以上版本中提供.
很多编程语言中都提供了一些散列和校验和函数,所以还可以使用数据库之外的散列和校验和函数。
计算散列和校验和是CPU密集型的操作,其计算代价很昂贵,但是其“网络友好性比较好”。
下面的ORA_ROWSCN方法不仅很小(类似于散列),而且计算时不是cpu密集的。
3、使用ORA_ROWSCN的乐观锁定
从Oracle 10.1开始,还可以使用内置的ORA_ROWSCN函数,其原理与版本列技术很相似,但是可以由Oracle自动执行,而不需要在表中增加额外的列,也不需要额外的代码来更新维护这个值。
四、最后
不管是悲观锁定还是乐观锁定都可以利用select for update nowaut查询来验证行未被修改。
悲观锁定会在用户有意修改数据那一刻使用这条语句。
乐观锁定则在即将在数据库中更新数据时使用这条语句。
这样不仅能解决应用中的阻塞问题,还可以修正数据完整性问题。