登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

梦想之鹰的天空

天高任鸟飞......放飞....心情..........放飞.....梦想

 
 
 

日志

 
 

在Oracle中如何实现读锁(二)  

2008-06-09 09:27:31|  分类: 数据库 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

  这一篇介绍一种更高级的实现方法,这使得无论是通过存储过程还是直接SQL访问都可以实现读锁的机制。

  在Oracle中如何实现读锁(一),上一篇文章给出了一种简单的方法,但是缺点也十分明显,就是要求用户必须采用调用函数的方式才能实现读锁。

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

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

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

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

  SQL> DECLARE

  2 V_LOCK NUMBER;

  3 BEGIN

  4 V_LOCK := DBMS_LOCK.REQUEST(0, RELEASE_ON_COMMIT => TRUE);

  5 END;

  6 /

  PL/SQL 过程已成功完成。

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

  SQL2> DECLARE

  2 V_LOCK NUMBER;

  3 BEGIN

  4 V_LOCK := DBMS_LOCK.REQUEST(0, RELEASE_ON_COMMIT => TRUE);

  5 END;

  6 /

  直到会话1提交、回滚或明确的释放锁资源:

  SQL> COMMIT;

  提交完成。

  会话2才解锁:

  PL/SQL 过程已成功完成。

  SQL2> COMMIT;

  提交完成。

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

  SQL> SELECT OBJECT_ID FROM USER_OBJECTS WHERE OBJECT_NAME = 'T';

  OBJECT_ID

  ----------

  93789

  SQL> CREATE OR REPLACE FUNCTION F_POLICY(OBJECT_SCHEMA IN VARCHAR2, OBJECT_NAME IN VARCHAR2)

  2 RETURN VARCHAR2 AS

  3 V_NUM NUMBER;

  4 BEGIN

  5 RETURN 'DBMS_LOCK.REQUEST(93789, 6, 60) IN (0, 4)';

  6 END;

  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> SELECT COUNT(*) FROM T;
  COUNT(*)
  ----------
  23
  SQL> COMMIT;

  提交完成。

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

  SQL2> SELECT COUNT(*) FROM T;

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


  SQL> SELECT DBMS_LOCK.RELEASE(93789) FROM DUAL;
  DBMS_LOCK.RELEASE(93789)
  ------------------------
  0

  会话2被解锁:


  COUNT(*)
  ----------
  23
  SQL2> SELECT DBMS_LOCK.RELEASE(93789) FROM DUAL;
  DBMS_LOCK.RELEASE(93789)
  ------------------------
  0

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


  SQL> CREATE OR REPLACE FUNCTION F_POLICY(OBJECT_SCHEMA IN VARCHAR2, OBJECT_NAME IN VARCHAR2)
  2 RETURN VARCHAR2 AS
  3 V_NUM NUMBER;
  4 BEGIN
  5 V_NUM := DBMS_LOCK.REQUEST(93590, 6, 60, TRUE);
  6 RETURN V_NUM || ' IN (0, 4)';
  7 END;
  8 /

  函数已创建。


  SQL> SELECT COUNT(*) FROM T;
  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的功能还不是很了解。

  评论这张
 
阅读(532)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018