Oracle的dbms_lock包主要用来控制并发,某些并发程序,在高并发的情况下,必须控制好并发请求的运行时间和次序,来保证处理数据的正确性和完整性。
某些并发程序,只需要对其中某几个参数做并发控制,例如时间段,OU等。对于这种业务需求,可以使用标准包DBMS_LOCK,把需要做并发控制的参数,加上锁,实现并发控制。
关键函数解析
创建或得到同名锁的句柄信息
DBMS_LOCK.ALLOCATE_UNIQUE (
lockname IN VARCHAR2, --想要获得的锁名称
lockhandle OUT VARCHAR2, --返回的锁句柄信息
expiration_secs IN INTEGER DEFAULT 864000); --距离最后一次分配多久后自动释放该锁
其中:
lockname: 产生唯一的LockID,大小不超过128B,大小写敏感。不能以’ORA$’字符串开头;如果lockname已分配LockID,则返回handle;否则,生成一个新的LockID,并返回handle
Lockhandle:返回值,request,convert,release调用。
expiration_secs:执行’allocate_unique’后,Clean Up的时间间隔。
使用句柄申请锁
dbms_lock.request(lockhandle IN VARCHAR2,
lockmode IN INTEGER DEFAULT x_mode,
timeout IN INTEGER DEFAULT maxwait,
release_on_commit IN BOOLEAN DEFAULT FALSE) RETURN INTEGER;
其中:
Lockhandle :allocate_unique取得的handle。
Lockmode :加锁模式。
Timeout :转换等待时间。
release_on_commit:在commit或者rollback之后释放锁,默认False。
对现有的锁进行模式转换 CONVERT
dbms_lock.convert(lockhandle IN VARCHAR2,
lockmode IN INTEGER,
timeout IN NUMBER DEFAULT maxwait) RETURN INTEGER;
其中:
Lockhandle:allocate_unique取得的handle。
Lockmode :转换的锁模式。
Timeout :转换等待时间,如果在指定的时间内得不到锁,则返回1 请求(超时)
返回值: 0 Success | 1 Timeout | 2 Deadlock | 3 Parameter error | 4 Don’t own lock specified by id or lockhandle | 5 Illegal lock handle
释放锁 RELEASE
dbms_lock.release(lockhandle IN VARCHAR2) RETURN INTEGER;
参数:Lockhandle:allocate_unique取得的handle
返回值: 0 Success | 3 Parameter error | 4 Don’t own lock specified by id or lockhandle |5 Illegal lock handle
Session睡眠
DBMS_LOCK.SLEEP (seconds IN NUMBER); --休眠时长(秒)
用法实例
DECLARE
l_lockname VARCHAR2(100);
l_lockhandle VARCHAR2(200);
l_lock_output NUMBER;
l_locked BOOLEAN := FALSE;
BEGIN
l_lockname := 'CUX2PXCALC' || p_organization_id;
/*创建锁对应的句柄信息*/
dbms_lock.allocate_unique(lockname => l_lockname, lockhandle => l_lockhandle);
/*用 l_lockhandle 这个唯一标识符给当前请求加锁,返回值如下~
Return value:
0 Success
1 timeout
2 deadlock
3 Parameter error
4 Don't own lock specified by id or lockhandle
5 Illegal lock handle*/
l_lock_output := dbms_lock.request(l_lockhandle, 6, 300, FALSE);
--锁定失败,则报错。
IF l_lock_output <> 0 THEN
x_return_status := fnd_api.g_ret_sts_error;
cux_2_fnd_api.set_message(p_app_name => 'FND', p_msg_name => 'CONC-LOCKED');
RAISE fnd_api.g_exc_error;
END IF;
l_locked := TRUE;
--此处添加请求的业务逻辑
--dbms_lock.sleep(seconds => 50);
/*特别注意的是一定要将lockname释放掉 否则这个并发就永远别想再执行了*/
IF l_locked THEN
l_lock_output := dbms_lock.release(l_lockhandle);
END IF;
EXCEPTION
WHEN fnd_api.g_exc_error THEN
IF l_locked THEN
l_lock_output := dbms_lock.release(l_lockhandle);
END IF;
END;