当参数相同时锁定程序不运行,但在参数不同时允许运行

Posted

技术标签:

【中文标题】当参数相同时锁定程序不运行,但在参数不同时允许运行【英文标题】:lock a Procedure from running when parameter are same but allow when parameters are different 【发布时间】:2021-12-14 10:30:15 【问题描述】:

如何防止过程在参数相同时运行,而在参数不同时允许。让我解释一下确切的问题

创建构建脚本

drop table datalock_test;
create table datalock_test
(year_ number) tablespace wad;
drop TYPE test_lock_type_arr;
drop TYPE test_lock_type;
create or replace TYPE test_lock_type AS OBJECT
                   (
                    year_ number
                    );
/                    
create or replace TYPE test_lock_type_arr IS TABLE OF test_lock_type
;
/
drop procedure insert_to_lock_test;
create or replace procedure insert_to_lock_test (p_year number)
as 
    l_arr test_lock_type_arr := test_lock_type_arr();
begin
select test_lock_type(year_) bulk collect into l_arr from (select p_year as year_ from dual) a
where not exists (select NULL from  datalock_test  b
where a.year_ = b.year_);
dbms_lock.sleep(15);
forall i IN l_arr.first .. l_arr.last SAVE EXCEPTIONS
insert  into datalock_test
values
(
l_arr(i).year_
);

commit;
end;
/
truncate table datalock_test;

正常测试:-

现在如果我在下面运行代码,将插入一条记录

begin
insert_to_lock_test(p_year => 1999);
end;
/

现在,如果我再次重新运行上述代码,则不会插入任何记录。

硬测试:-

但如果我在下面运行,则会插入重复的记录,这是不可取的。

begin
dbms_scheduler.create_job (
job_name => 'load1',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2000);
end;',
enabled => true);
dbms_scheduler.create_job (
job_name => 'load2',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2000);
end;',
enabled => true);
end;
/

这种重复是我必须避免的。

约束:-

    我无法创建唯一索引或主键。 我不能使用任何额外的检查和存在条件。

我尝试过的:-

    已尝试独占锁定。但这里的问题是它也阻止了以下功能。在以下情况下,参数不同,因此进程应该能够并行运行。让我提供有关如何完成此操作的代码

重新创建过程如下

create or replace procedure insert_to_lock_test (p_year number)
as 
    l_arr test_lock_type_arr := test_lock_type_arr();
    lv_lockhandle VARCHAR2(500);
    lv_ret_code   PLS_INTEGER;
    lv_retcode    NUMBER;
    p_nm varchar2(200) := 'testlock';
begin
dbms_lock.allocate_unique(p_nm, lv_lockhandle);
    lv_retcode := dbms_lock.request(lockhandle=>lv_lockhandle, 
                                 lockmode => dbms_lock.x_mode);
select test_lock_type(year_) bulk collect into l_arr from (select p_year as year_ from dual) a
where not exists (select NULL from  datalock_test  b
where a.year_ = b.year_);
dbms_lock.sleep(15);
forall i IN l_arr.first .. l_arr.last SAVE EXCEPTIONS
insert  into datalock_test
values
(
l_arr(i).year_
);

commit;
lv_ret_code := dbms_lock.release(lv_lockhandle);
end;
/

发布这个让我们以相同的参数并行运行代码

begin
dbms_scheduler.create_job (
job_name => 'load1',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2000);
end;',
enabled => true);
dbms_scheduler.create_job (
job_name => 'load2',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2000);
end;',
enabled => true);
end;
/

没有重复插入,只有一条记录,这是可取的。但现在的问题是当我们在代码下面运行时

begin
dbms_scheduler.create_job (
job_name => 'load1',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2020);
end;',
enabled => true);
dbms_scheduler.create_job (
job_name => 'load2',
job_type => 'plsql_block',
job_action => 'begin
insert_to_lock_test(p_year => 2021);
end;',
enabled => true);
end;
/

这里插入2021耗时太长,参数不同时延迟不理想。

【问题讨论】:

你想解决什么问题 让我们假设一个 SP1 正在运行,参数 a,b,c 传递给它。现在,如果我尝试使用参数 a、b、c 再次运行 SP1,那么它不应该允许我,但是如果我使用参数 p、q、r 运行 SP1,那么它应该运行。这是我要解决的问题 你错过了我的问题。为什么不希望它们同时运行。你想防止什么问题?请提供一个同时运行时遇到的问题的工作示例 “这是我要解决的问题” 这就是您要解决的技术问题,作为一些解决方案业务问题。与其将时间浪费在构思不周的技术解决方案上,不如说明业务问题并让社区致力于最佳技术解决方案。总之,你问的是经典x-y question 业务流程可能非常简单@EdStevens。该过程根据参数值执行不同的任务。该过程可以从多个并发会话中调用,但所有执行的参数值必须不同 - 必须阻止尝试使用当前正在运行的参数值进行调用。 【参考方案1】:

你的问题出在参数timeout => 0的设置上

documentation 说

继续尝试授予锁定的秒数。如果在此时间段内无法授予锁,则调用返回值 1(超时)。

在零秒内,您可能会或可能不会收到超时,因此有时您不会被阻止,有时您会被阻止。

删除timout 参数(并使用默认值MAXWAIT将其设置为某个实际值检查响应值 - 如果是@987654325 @你有一个超时,必须处理它。你桅杆处理所有的回报!= 0。

我一般你应该经常检查request中的返回码,最好还部署一些逻辑来检测悬挂把手并释放它们(例如设置release_on_commit

示例

注意位更新过程。 尽管删除了timeout 参数,我还是检查了request 函数的返回码。

最后我添加了p_wait 参数,这样我就可以在长时间等待的情况下摆脱阻塞过程,而无需等待测试过程,我可以清楚地看到行为,它也记录在过程的总经过时间中。

一切正常 - 见下文

create or replace procedure testlockProc (p_nm varchar2, p_wait int default 0)
as
lv_lockhandle VARCHAR2(500);
    lv_ret_code   PLS_INTEGER;
    lv_retcode    NUMBER;
    request_failed EXCEPTION;
    lv_start DATE;
begin
    lv_start := sysdate;
    dbms_lock.allocate_unique(p_nm, lv_lockhandle);
    DBMS_OUTPUT.PUT_LINE (' got handle '|| lv_lockhandle);
    lv_retcode := dbms_lock.request(lockhandle=>lv_lockhandle, /**timeout => 0,**/
                                 lockmode => dbms_lock.x_mode);
    if lv_retcode != 0 then
       raise request_failed;
    end if;
    DBMS_OUTPUT.PUT_LINE ('request got REGEST response '|| lv_retcode);                                
    dbms_lock.sleep(p_wait);
    lv_ret_code := dbms_lock.release(lv_lockhandle);
    DBMS_OUTPUT.PUT_LINE ('release got REGEST response '|| lv_retcode|| ' in ' || to_char( round((sysdate-lv_start)*24*3600))|| ' seconds');   
end;
/

-- session 1 - procedure blocks the parameter for 60 seconds
set serveroutput on
begin
testlockProc (p_nm => 'wow', p_wait => 60);
end;
/

got handle 1073741964107374196448
request got REGEST response 0
release got REGEST response 0 in 60 seconds


-- session 2  (procedure blocked due to identical parameter from session 1)

set serveroutput on
begin
testlockProc (p_nm => 'wow');
end;
/

got handle 1073741964107374196448
request got REGEST response 0
release got REGEST response 0 in 50 seconds

-- session 3 different parameter (handle) - runns immediately

set serveroutput on
begin
testlockProc (p_nm => 'wow23');
end;
/

got handle 1073741965107374196549
request got REGEST response 0
release got REGEST response 0 in 0 seconds

【讨论】:

谢谢 Marmite,我要解决的问题是。让我们假设一个存储过程 SP1 正在使用传递给它的参数 a、b、c 运行。现在,如果我尝试使用参数 a、b、c 再次运行 SP1,那么它不应该允许我,锁应该等到超时。但是如果我使用参数 p,q,r 运行 SP1,那么它应该运行它应该能够获得锁。 @sakeesh step 1) increate the timeout, to get timeout in zeor seconds is nothing, step 2) check return code from request如果不等于零,则引发异常。比锁定将按预期工作! 谢谢。这看起来有效

以上是关于当参数相同时锁定程序不运行,但在参数不同时允许运行的主要内容,如果未能解决你的问题,请参考以下文章

Cordova 应用程序在 safari 上运行良好,但在作为应用程序安装时崩溃

Linux基本指令(持续更新中..)

当两个方法同名但参数不同时如何在VB.Net中实现一个接口

C程序在GDB中工作,单独运行时崩溃

位置更新不适用于允许运行时权限

存储过程如何在从 BIDS 调用时不返回行,但在使用相同参数时从 SSMS 调用时返回行?