悲观与乐观锁详解
初三 其它 2155字 240人浏览 董济娟

Oracle 数据库锁定:悲观与乐观锁详解 2012年01月31日21:36 来源:中国IT 实验室 作者:佚名 编辑:王玉圆 评论:0条

【IT168 技术】Oracle 数据库悲观锁与乐观锁是本文我们主要要介绍的内容。有时候为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定……

以下是代码片段:

select * from test where id = 10

也就是没有for update这种锁定数据的语句的话,就不会造成阻塞了。另外一种情况,就是当数据库数据被锁定的时候,也就是执行刚才for update那条sql 以后,我们在另外一个session 中执行for update nowait 后又是什么样呢。比如如下的sql 语句。 由于这条语句中是制定采用nowait 方式来进行检索,所以当发现数据被别的session 锁定中的时候,就会迅速返回ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源。所以在程序中我们可以采用nowait 方式迅速判断当前数据是否被锁定中,如果锁定中的话,就要采取相应的业务措施进行处理。

以下是代码片段:

select * from test where id = 10 for update nowait

那这里另外一个问题,就是当我们锁定住数据的时候,我们对数据进行更新和删除的话会是什么样呢。比如同样,我们让第一个Session 锁定住id=10的那条数据,我们在第二个session 中执行如下语句。

以下是代码片段:

update test set value=2 where id = 10

这个时候我们发现update 语句就好像select for update语句一样也停住卡在这里,当你第一个session 放开锁定以后update 才能正常运行。当你update 运行后,数据又被你update 语句锁定住了,这个时候只要你update 后还没有commit, 别的session 照样不能对数据进行锁定更新等等。

总之,Oracle 中的悲观锁就是利用Oracle 的Connection 对数据进行锁定。在Oracle 中,用这种行级锁带来的性能损失是很小的,只是要注意程序逻辑,不要给你一不小心搞成死锁了就好。而且由于数据的及时锁定,在数据提交时候就不呼出现冲突,可以省去很多恼人的数据冲突处理。缺点就是你必须要始终有

一条数据库连接,就是说在整个锁定到最后放开锁的过程中,你的数据库联接要始终保持住。与悲观锁相对的,我们有了乐观锁。乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。 在乐观锁中,我们有3种常用的做法来实现:

[1]第一种就是在数据取得的时候把整个数据都copy 到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。

[2]第二种乐观锁的做法就是采用版本戳,这个在Hibernate 中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table 上建立一个新的column, 比如为number 型,当你数据每更新一次的时候,版本数就会往上增加1. 比如同样有2个session 同样对某条数据进行操作。两者都取到当前的数据的版本号为1, 当第一个session 进行数据更新后,在提交的时候查看到当前数据的版本还为1, 和自己一开始取到的版本相同。就正式提交,然后把版本号增加1, 这个时候当前数据的版本为2.

当第二个session 也更新了数据提交的时候,发现数据库中版本为2, 和一开始这个session 取到的版本号不一致,就知道别人更新过此条数据,这个时候再进行业务处理,比如整个Transaction 都Rollback 等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器) 来进行验证。不过数据库的Trigger 的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用Trigger.

[3]第三种做法和第二种做法有点类似,就是也新增一个Table 的Column, 不过这次这个column 是采用timestamp 型,存储数据最后更新的时间。在

Oracle9i 以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp 的数据精度在Oracle 的时间类型中是最高的,精确到微秒(还没与到纳秒的级别) ,一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK, 否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger 或者存储过程中。