Oracle 悲观锁和乐观锁

本文深入探讨了多用户环境中常见的并发控制问题,特别是丢失更新的问题及其解决方案。通过介绍悲观锁和乐观锁两种主要策略,详细解释了如何在数据库操作中防止数据冲突。悲观锁通过在更新前锁定数据来确保数据一致性,而乐观锁则通过版本号或时间戳等机制在最后阶段检查数据是否已被修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.丢失更新
   
所有多用户环境中存在丢失更新,丢失更新可以如下示例:
   

        发生这种情况的原因:应用开发人员编写的程序时是这样的:更新一个特定的字段时,该记录的所有字段都会“刷新”(只是因为更新所有列更容易,这样就不用先得出哪些列已经修改,并且只更新那些修改过的列)。

       避免这种情况可以用两种锁定策略:悲观锁定或乐观锁定。

二.悲观锁
     
悲观锁定只用于有状态或有连接环境
     在试图更新之前,从数据库查询这一行就锁定这一行。
(因为我们很悲观,对于这一行能不能保持未改变很是怀疑)
  
  select empno,ename,sal from scott.emp where deptno=10;
  

EMPNO  ENAME   SAL
    7782    CLARK    2450.00
    7839    KING       5000.00
    7934    MILLER    1300.00


  在SQLplus中模拟应用可能执行的绑定调用,可以利用下面命名:
 SQL> variable empno number
SQL> variable ename varchar2(20)
SQL> variable sal number
SQL> exec :empno :=7934; :ename :='MILLER'; :sal :=1300;
 
PL/SQL procedure successfully completed


 for update 锁定这一行
 
The FOR UPDATE clause lets you lock the selected rows so that other users cannot lock or update the rows until you end your transaction. You can specify this clause only in a top-level SELECT statement, not in subqueries.
 
锁定命令:
 SQL>  select empno,ename,sal from scott.emp
  2  where empno=:empno and ename=:ename and sal=:sal
  3  for update nowait;

EMPNO ENAME            SAL
----- ---------- ---------
 7934 MILLER       1300.00

试图对这行进行修改
SQL>update scott.emp set ename='ML' where empno=7934;
进程耗在这,因为这行被你锁定了,在你结束你的事务之前,不能修改


查询锁定对象:
SELECT Do.Object_Name, Session_Id, Process, Locked_Mode
FROM V$locked_Object  Lo, Dba_Objects Do
WHERE Lo.Object_Id = Do.Object_Id;

OBJECT_NAM SESSION_ID PROCESS      LOCKED_MODE
  ----------            ----------            ------------           -----------
    EMP                 140                   3204:3008              3

提交事务,锁释放。之后可以进行更新


三.乐观锁
    乐观锁:把所有锁定都延迟到即将执行更新之前才做。换句话说,我们会修改信息而不需要锁。
  
(我们很乐观,认为数据不会被其他用户修改,因此,会等到最后一刻才去看想法对不对)

   实现乐观并发控制的有很多种;
(1)可以在应用中同时保留旧值和新值,然后再更新数据时使用如下语句,这是乐观锁的一种流行实现:
    update table
       set column1 =:new_column1, column2 =:new column2,...
    where primary_key=:primary_key
    and column1 =:old_column1
    and column2 =:old_column2
    ........
 
 
  (2)使用一个特殊的列记录的"版本",这个列由数据库触发器或应用程序代码维护。
 例子: (先建一个表,然后插入数据,在更新时如果实现乐观锁)
 create table carleena.dept
( deptno number(2),
  dname  varchar2(14),
  loc    varchar2(13),
  last_mod timestamp with time zone default systimestamp not null,
  constraint dept_pk primary key(deptno) 
)


插入数据
  insert into carleena.dept(deptno,dname,loc)
  select deptno,dname,loc
  from scott.dept;


进行相关变量的绑定
SQL> variable deptno number;
SQL> variable dname varchar2(14);
SQL> variable loc varchar2(13);
SQL> variable last_mod varchar2(50);
SQL> begin
  2    :deptno :=10;
  3   select dname,loc,last_mod
  4   into :dname,:loc,:last_mod
  5    from carleena.dept
  6  where deptno=:deptno;
  7  end;
  8  /
 PL/SQL procedure successfully completed


deptno    dname                loc              last_mod
  ----------    ----------            ---------              ---------- 
  10          ACCOUNTING  NEW YORK   06-3月 -10 08.55.35.609000 下午 +08:00

执行更新(最后一行执行一个非常重要的检查,以确保时间戳一样,last_mod值一样说明没有进行修改,可以更新)
  SQL> update carleena.dept
  2  set dname = 'AC',
  3  last_mod =systimestamp
  4  where deptno =:deptno
  5  and last_mod =to_timestamp_tz(:last_mod);
 
 1 row updated

执行上面相同逻辑的更新,但是last_mod仍然使用原来绑定的值,没有获取新的值,这时最后一行的值不匹配,故不能进行更新。
  SQL> update carleena.dept
  2  set dname = 'AC1',
  3  last_mod =systimestamp
  4  where deptno =:deptno
  5  and last_mod =to_timestamp_tz(:last_mod);

  0 rows updated

乐观锁定还有其他实现方法


四. 总结:


    注意:两个T1并不是指同一时刻,两个T2也一样不是指同一时刻。
   只是表示一个时间序列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值