通过dbms_lock了解oracle内部enqueue机制
同时被调用的共享代码如下
create or replace function fun_wxc(p1 in varchar2) return number-- result_cache is rs number; begin rs := p1; sys.dbms_lock.sleep(25); return rs; end;
锁代码如下
create or replace procedure p_enqueue_test(p_lock_mode/*请求获取的锁模式*/ number, p_lock_expired/*请求等待超时时间*/ number) is l_name varchar2(100); l_handle varchar2(100); l_is_get number; f_name varchar2(240) := 'fun_wxc';--要调用代码片段的名称 p_p1 number := 2; begin --打印会话信息 dbms_output.put_line('--------------------->session' ||testseq.nextval||'<---------------------'); /*lockname 类似于定义一个资源的名称,用于并发控制程序判断当前这个资源有没有被锁定*/ l_name := f_name || '_' || p_p1; dbms_lock.allocate_unique(lockname => l_name, lockhandle => l_handle); dbms_output.put_line('the lock handle is ' || l_handle); dbms_output.put_line('lock request start time is ' || to_char((sysdate), 'yyyy/mm/dd hh24:mi:ss')); --锁请求,若果代码已经被执行,锁请求等待xx s后超时退出 l_is_get := dbms_lock.request(l_handle, p_lock_mode, p_lock_expired, false); --判锁获取是否成功,不成功则不执行我们的代码 if l_is_get <> 0 then dbms_output.put_line('the procedure is executing,can not call it in the same time'); end if; if l_is_get = 0 then dbms_output.put_line('the lock request seccuss,execute start time is ' || to_char((sysdate), 'yyyy/mm/dd hh24:mi:ss')); ----------------------------------------------------------------- /*要执行的代码开始*/ declare r1 number; begin r1 := fun_wxc(2); /*要执行的代码结束*/ dbms_output.put_line('the procedure has been executed,finish time is ' || to_char((sysdate), 'yyyy/mm/dd hh24:mi:ss')); end; --执行完成后释放锁 l_is_get := dbms_lock.release(l_handle); end if; end;
同时开5个sqlplus 窗口,模拟5个并发会话要同时执行函数fun_wxc的场景
开始后5个会话依次完成,结果如下
会话1以共享模式获取资源fun_wxc上的锁,成功
会话2以共享模式获取资源fun_wxc上的锁,因为锁模式是兼容的,所以可以成功获取,并且与会话1几乎同时完成,如下时间所示
会话3以排他模式获取资源fun_wxc上的锁,因为锁模式是不兼容的,所以可以被阻塞在等待者队列中,到会话1,2完成后释放资源,最终获取到了锁资源并且成功执行,如下时间所示
会话4以共享模式获取资源fun_wxc上的锁,因为锁模式是不兼容的,所以可以被阻塞在等待者队列中,到会话3完成后释放资源,最终获取到了锁资源并且成功执行,如下时间所示
会话5以排他模式获取资源fun_wxc上的锁,因为锁模式是不兼容的,所以可以被阻塞在等待者队列中,但是等待超时了,所以没有执行如下时间所示
在v$lock 并中也可以监控到执行时候的排队情况,如下