WEB开发网
开发学院数据库Oracle 怎么再Oracle中进行读锁 阅读

怎么再Oracle中进行读锁

 2008-12-18 13:07:34 来源:WEB开发网   
核心提示:很多情况下,上面的条件是无法实现的,怎么再Oracle中进行读锁,这就要求必须有一种方法对于所有的访问情况都试用, 现在面临两个难题,如果某个功能你在Oracle中无法实现,那么并不意味着在Oracle中无法实现,一个是Oracle的读不加锁,因此必须自己实现锁的功能

很多情况下,上面的条件是无法实现的,这就要求必须有一种方法对于所有的访问情况都试用。

现在面临两个难题,一个是Oracle的读不加锁,因此必须自己实现锁的功能,二是如何将锁的实现添加到SELECT语句中,普通的触发器不会被SELECT所触发,因此通过触发器来实现这个功能是不现实的。

对于第一个问题,可以通过Oracle的DBMS_LOCK包来实现定制用户自定义锁的实现,而第二个问题可以利用Oracle的精细访问控制来实现。

简单描述一下思路,利用DBMS_LOCK.REQUEST过程,指定一个ID,来获取独占锁,其他会话获取同样的锁就会被锁定:

  SQL>DECLARE
  2V_LOCKNUMBER;
  3BEGIN
  4V_LOCK:=DBMS_LOCK.REQUEST(0,RELEASE_ON_COMMIT=>TRUE);
  5END;
  6/

PL/SQL 过程已成功完成。

会话2获取同样的锁,就会被锁定:

  SQL2>DECLARE
  2V_LOCKNUMBER;
  3BEGIN
  4V_LOCK:=DBMS_LOCK.REQUEST(0,RELEASE_ON_COMMIT=>TRUE);
  5END;
  6/

SQL> COMMIT;

提交完成。

会话2才解锁:

PL/SQL 过程已成功完成。

SQL2> COMMIT;

提交完成。

利用DBMS_LOCK包可以实现锁的功能,下面就是利用DBMS_RLS包添加精细访问策略,在访问目标表的时候,将锁添加到查询语句中,简单的实现如下:

  SQL>SELECTOBJECT_IDFROMUSER_OBJECTSWHEREOBJECT_NAME='T';
  OBJECT_ID
  ----------
  93789
  SQL>CREATEORREPLACEFUNCTIONF_POLICY(OBJECT_SCHEMAINVARCHAR2,OBJECT_NAMEINVARCHAR2)
  2RETURNVARCHAR2AS
  3V_NUMNUMBER;
  4BEGIN
  5RETURN'DBMS_LOCK.REQUEST(93789,6,60)IN(0,4)';
  6END;
  7/

函数已创建。

SQL> EXEC DBMS_RLS.ADD_POLICY(USER, 'T', 'MYPOLICY', USER, 'F_POLICY');

PL/SQL 过程已成功完成。

利用T的OBJECT_ID作为锁ID,避免和其他对象获取锁发生冲突,由于DBMS_LOCK.REQUEST过程的RELEASE_ON_COMMIT参数要求布尔类型,而布尔类型无法在SQL中使用,这里暂时使用默认值FALSE。将这个函数作为T表的访问策略添加成功后,访问T表时,Oracle会自动将DBMS_LOCK.REQUEST(93789, 6, 60) IN (0, 4)放到WHERE语句之后,从而实现读锁的功能:

  SQL>SELECTCOUNT(*)FROMT;
  COUNT(*)
  ----------
  23
  SQL>COMMIT;

提交完成。

会话2对T表的查询被锁定:

SQL2> SELECT COUNT(*) FROM T;

由于没有指定RELEASE_ON_COMMIT为TRUE,会话1提交或回滚仍然会占有锁资源,只有断开会话或明确的释放锁资源:

  SQL>SELECTDBMS_LOCK.RELEASE(93789)FROMDUAL;
  DBMS_LOCK.RELEASE(93789)
  ------------------------
  0

会话2被解锁:

  COUNT(*)
  ----------
  23
  SQL2>SELECTDBMS_LOCK.RELEASE(93789)FROMDUAL;
  DBMS_LOCK.RELEASE(93789)
  ------------------------
  0

解决这个问题的方法是修改函数,由于这个函数调用发生在查询之前,因此将锁定放到函数中结果是一样的:

SQL>CREATEORREPLACEFUNCTIONF_POLICY(OBJECT_SCHEMAINVARCHAR2,OBJECT_NAMEINVARCHAR2)

2RETURNVARCHAR2AS

3V_NUMNUMBER;

4BEGIN

5V_NUM:=DBMS_LOCK.REQUEST(93590,6,60,TRUE);

6RETURNV_NUM||'IN(0,4)';

7END;

8/

函数已创建。

  SQL>SELECTCOUNT(*)FROMT;
  COUNT(*)
  ----------
  23

会话2尝试查询T表:

SQL2> SELECT COUNT(*) FROM T;

这时会话1可以通过提交或回滚来释放锁:

SQL> COMMIT;

提交完成。

会话2获取锁资源并查询T表记录:

  COUNT(*)
  ----------
  23
  SQL2>COMMIT;

提交完成。

这样,通过DBMS_LOCK包自定义锁和DBMS_RLS包设置精细访问策略实现了Oracle中的读锁功能。需要注意的是,这种方法对于SYS用户无效,因为SYS用户不受精细访问策略的影响。

个人比较欣赏Tom的那句话,在Oracle中,很少会说不能做什么,而是会有你用多少中选择来实现这个功能。

这里想加一句,如果某个功能你在Oracle中无法实现,那么并不意味着在Oracle中无法实现,而多半是你对Oracle的功能还不是很了解。

Tags:怎么 Oracle 进行

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接