当参数相同时从 运行 锁定过程,但当参数不同时允许
lock a Procedure from running when parameter are same but allow when parameters are different
如何在参数相同时阻止过程 运行ning 并在参数不同时允许。让我解释一下确切的问题
正在创建构建脚本
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;
/
Post 这让 运行 代码与相同的参数并行
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耗时过长,参数不同时延迟不可取
你的问题出在参数设置上timeout => 0
Number of seconds to continue trying to grant the lock. If the lock cannot be granted within this time period, then the call returns a value of 1 (timeout).
在零秒内你可能会或可能不会超时,所以有时你没有被阻止,有时你是。
删除timout
参数(并使用默认MAXWAIT
)或将其设置为一些实际值和 检查响应值 - 如果是 1
你有超时并且必须处理它。您必须处理所有 returns != 0.
我一般 你应该经常检查 request
中的 return 代码,最好还部署一些逻辑来检测挂起的句柄并释放它们(例如设置 release_on_commit
)
例子
注意位更新过程。
尽管删除了 timeout
参数,但我检查了 request
函数的 return 代码。
最后我添加了 p_wait
参数,这样我就可以安排 blocking 过程和 testing没有等待的程序,我可以清楚地看到行为,这也记录在程序的总运行时间中。
一切正常 - 见下文
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
如何在参数相同时阻止过程 运行ning 并在参数不同时允许。让我解释一下确切的问题
正在创建构建脚本
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;
/
Post 这让 运行 代码与相同的参数并行
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耗时过长,参数不同时延迟不可取
你的问题出在参数设置上timeout => 0
Number of seconds to continue trying to grant the lock. If the lock cannot be granted within this time period, then the call returns a value of 1 (timeout).
在零秒内你可能会或可能不会超时,所以有时你没有被阻止,有时你是。
删除timout
参数(并使用默认MAXWAIT
)或将其设置为一些实际值和 检查响应值 - 如果是 1
你有超时并且必须处理它。您必须处理所有 returns != 0.
我一般 你应该经常检查 request
中的 return 代码,最好还部署一些逻辑来检测挂起的句柄并释放它们(例如设置 release_on_commit
)
例子
注意位更新过程。
尽管删除了 timeout
参数,但我检查了 request
函数的 return 代码。
最后我添加了 p_wait
参数,这样我就可以安排 blocking 过程和 testing没有等待的程序,我可以清楚地看到行为,这也记录在程序的总运行时间中。
一切正常 - 见下文
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