我在 Oracle 11g RAC 环境中维护一些存储过程,这些过程是与其他系统进行数据交换的步骤管道的一部分。特别是其中之一一直在产生重复的记录。文件由 ETL 作业加载到一组暂存表中,然后调用这个没有参数的存储过程(除了被调用 ETL 代码忽略的 OUT 参数,但这是另一个问题)。该过程的主体以如下语句开头
For I in ( SELECT * from SOME_STAGING_TABLE where status = 'NEW' order by X_ID) LOOP
Run Code Online (Sandbox Code Playgroud)
但是,调用它的 ETL 作业又被另一个 ETL 作业调用,该作业查找所有尚未处理的“文件”并并行调用它,每个文件一次。我认为正在发生的事情是:
一种解决方案可能是在 ETL 服务中强制执行序列化。另一种可能是更改存储过程的调用约定,以便将文件 ID 作为参数。但是,假设我需要在数据库中执行此操作。有没有办法设置存储过程,以便一次只有一个连接可以执行其中的指定代码块?
(在 C 或 Java 中,这将被称为“互斥锁”或“同步块”,我知道 MySQL 有一个“LOCK TABLES”语句可用于该目的;Oracle 11g 有类似的功能吗?)
Oracle 有多种锁定机制可用于序列化操作:
DBMS_LOCK.request
。此锁定可以在会话期间或直到您调用DBMS_LOCK.release
。这个锁可以跨提交维护。使用DBMS_LOCK
以下序列化的示例:
SQL> CREATE OR REPLACE PROCEDURE serial IS
2 l_lock_handle VARCHAR2(128 BYTE);
3 l_lock_request INTEGER;
4 BEGIN
5 dbms_lock.allocate_unique(lockname=> 'MY_SERIAL_PROC', lockhandle => l_lock_handle);
6 LOOP
7 l_lock_request := dbms_lock.request(lockhandle => l_lock_handle,
8 timeout => 5,
9 release_on_commit => FALSE);
10 CASE l_lock_request
11 WHEN 0 THEN
12 EXIT; -- success
13 WHEN 1 THEN
14 dbms_output.put_line('lock already reserved, wait...');
15 dbms_lock.sleep(5); -- sleep 5 seconds before retrying
16 ELSE
17 raise_application_error(-20001, 'Lock error: ' || l_lock_request);
18 END CASE; --
19 END LOOP;
20 ----------------------------------------
21 -- serialized block of code --
22 -- (lock will be kept accross commit) --
23 ----------------------------------------
24 dbms_lock.sleep(30);
25 ----------------------------------------
26 -- End of serialized code --
27 ----------------------------------------
28 l_lock_request := dbms_lock.release(lockhandle => l_lock_handle);
29 END;
30 /
Procedure created
Run Code Online (Sandbox Code Playgroud)
只有一个会话可以同时运行此过程:
session1> exec serial;
session2> exec serial;
lock already reserved, wait...
lock already reserved, wait...
lock already reserved, wait...
PL/SQL procedure successfully completed
PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
11858 次 |
最近记录: |