在 Oracle PL/SQL 中创建“屏障”(防止并行执行)?

dav*_*vid 2 oracle locking

我在 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 作业调用,该作业查找所有尚未处理的“文件”并并行调用它,每个文件一次。我认为正在发生的事情是:

  1. 主 ETL 作业运行,找到两个文件,调用两个工作作业
  2. Worker ETL 作业 A 运行,通过第一阶段
  3. Worker ETL 作业 B 运行,通过第一阶段
  4. Worker ETL 作业 A 调用存储过程
  5. 存储过程 A' 获取两个文件的记录以进行处理
  6. Worker ETL 作业 B 调用存储过程
  7. 存储过程 B' 获取两个文件的记录来处理

一种解决方案可能是在 ETL 服务中强制执行序列化。另一种可能是更改存储过程的调用约定,以便将文件 ID 作为参数。但是,假设我需要在数据库中执行此操作。有没有办法设置存储过程,以便一次只有一个连接可以执行其中的指定代码块?

(在 C 或 Java 中,这将被称为“互斥锁”或“同步块”,我知道 MySQL 有一个“LOCK TABLES”语句可用于该目的;Oracle 11g 有类似的功能吗?)

Vin*_*rat 6

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)