使用DBMS_LOCK手动锁定

在PL/SQL中,我们有时候需要保证某个Procedure不被并发执行。一般采用select for update对一个固定数据表的数据行进行锁定。

除此之外,还可以使用dbms_lock包提供的request()和release()方法进行锁定。

 

下面例子的逻辑是,在插入A之前用count()判断,如果A中没有数据就插入。在高并发的环境下,没有锁定就可能造成多条插入。为了避免并发错误,使用dbms_lock进行手动控制。

drop table a;
create table a(x int);
drop table b;
create table b(sid number, dt timestamp default systimestamp);


create or replace procedure lock_thing is
l_result int;
l_cnt int;
begin
-- get the lock with ID=0
-- release_on_commit=false means keep lock until explicitly released or until end-of-session
l_result := dbms_lock.request(id => 0, release_on_commit => false);

-------------------------------------------
-- do your own logic here
-------------------------------------------
if l_result = 0 then
-- running log
insert into b(sid) values(sys_context('userenv','bg_job_id'));

select count(1) into l_cnt from a;
if l_cnt = 0 then
insert into a(x) values(sys_context('userenv','bg_job_id'));
end if;
end if;

commit;
-- explicitly release it
l_result := dbms_lock.release(id => 0);
exception
when others then
l_result := dbms_lock.release(id => 0);
end;
/

 用一个程序模拟并发调用procedure,最后的结果是A中仅有一条数据。(NOTE: 这个并发模拟并不严格)

-- use some backgound jobs to simulate concurrent calls
declare
l_job int;
begin
delete a;
delete b;
commit;
for i in 1 .. 100 loop
dbms_job.submit(job => l_job, what => 'begin lock_thing(); end;');
commit;
end loop;
end;
/

-- only one row in table A
select a.*, b.* from a, b where a.x(+)=b.sid order by b.sid;




posted on 2012-02-10 15:39  wait4friend  阅读(1071)  评论(0编辑  收藏  举报