当参数相同时锁定程序不运行,但在参数不同时允许运行
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 fromrequest
如果不等于零,则引发异常。比锁定将按预期工作!
谢谢。这看起来有效以上是关于当参数相同时锁定程序不运行,但在参数不同时允许运行的主要内容,如果未能解决你的问题,请参考以下文章