Oracle中根本没有读锁,楼主的要求和Oracle尽可能提高并发的目的是截然相反的。
写这篇文章的目的并不是说这个需求有什么普遍性,而是为了说明在Oracle中其实没有什么是做不到的,即使这个需求和Oracle的设计本意相违背,另外希望这篇文章中的一些思路能起到抛砖引玉的作用。
楼主提出的问题是“怎么样让一个表,一个时间只能一个人读”,简单概括一下就是建立起读锁的机制。而且这个读锁还不能是共享锁,而必须是有个独占锁。
考虑到Oracle中根本不存在读锁,那么必须将思路进行转化。
最先想到的是,将查询转化为DML,这样就可以获取到锁,避免其他用户对改对象同时进行访问。
最简单的实现方式莫过于建立一个存储过程,在存储过程中首先LOCK TABLE,然后进行查询,将查询的结果返回。
简单实现如下:
SQL> CREATE TABLE T
2 (
3 ID NUMBER PRIMARY KEY,
4 NAME VARCHAR2(30)
5 );
表已创建。
SQL> INSERT INTO T SELECT ROWNUM, TNAME FROM TAB;
已创建23行。
SQL> COMMIT;
提交完成。
下面构建函数:
SQL> CREATE OR REPLACE FUNCTION F_QUERY_T RETURN SYS_REFCURSOR AS
2 V_CURSOR SYS_REFCURSOR;
3 BEGIN
4 LOCK TABLE T IN EXCLUSIVE MODE;
5 OPEN V_CURSOR FOR 'SELECT * FROM T';
6 RETURN V_CURSOR;
7 END;
8 /
函数已创建。
SQL> SELECT F_QUERY_T FROM DUAL;
F_QUERY_T
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ID NAME
---------- ------------------------------
1 CHAINED_ROWS
2 DEPT
3 EMP
4 BONUS
5 SALGRADE
6 DUMMY
7 T_PK
8 S_T
9 MV_CAPABILITIES_TABLE
10 TB_OBJECT_1136
11 MLOG$_TB_OBJECT_1136
12 RUPD$_TB_OBJECT_1136
13 MV_TB_OBJECT_1136
14 ORD_ORDER
15 TT
16 MV_T
17 T_PRIMARY
18 T_UPDATE
19 T
20 INF_PRODUCT
21 INF_DRUG
22 T_OLD
23 INF_PRODUCT_PROPERTY
已选择23行。
下面在另外一个会话登陆,仍然通过函数来访问:
SQL> SET SQLP 'SQL2> '
SQL2> SELECT F_QUERY_T FROM DUAL;
会话被锁定,只有会话1提交或回滚,会话2才能继续查询:
SQL> COMMIT;
提交完成。
这时会话2解锁:
F_QUERY_T
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ID NAME
---------- ------------------------------
1 CHAINED_ROWS
2 DEPT
3 EMP
4 BONUS
5 SALGRADE
6 DUMMY
7 T_PK
8 S_T
9 MV_CAPABILITIES_TABLE
10 TB_OBJECT_1136
11 MLOG$_TB_OBJECT_1136
12 RUPD$_TB_OBJECT_1136
13 MV_TB_OBJECT_1136
14 ORD_ORDER
15 TT
16 MV_T
17 T_PRIMARY
18 T_UPDATE
19 T
20 INF_PRODUCT
21 INF_DRUG
22 T_OLD
23 INF_PRODUCT_PROPERTY
已选择23行。
SQL2> ROLLBACK;
回退已完成。
通过这种方法,简单的实现了读锁的功能,不过这种方法的缺点也很明显,要求用户必须通过函数的方式访问,而直接通过SQL方式访问是可以绕过锁机制的。