Sunteți pe pagina 1din 13

Oracle Session Traffic Lights

Silvia Marrone Oracle University Principal Trainer

You have written the code for a complex application, which requires synchronization between two or more sessions. Your system must also regulate the start of different applications based on the completion of certain events. For these requirements the ideal solution is the Oracle supplied package DBMS_LOCK. In this edition of OU Experts Corner we illustrate DBMS_LOCK and demonstrate its usage with an interesting example of synchronising sessions with an event. Overview The DBMS_LOCK package provides an interface to Oracle Lock Management services, allowing programmers to manage what Oracle refers to as user locks. Programmers and DBAs may request locks with unique names recognizable in other procedures in the same or other instances, choose the lock mode, change the lock mode and release the lock just by using a few simple procedures and functions. A user lock has all features of a database lock. User locks can help to: Synchronize applications and enforce sequential processing Provide exclusive access to a device, such as a terminal Provide application-level enforcement of read locks Detect when a lock is released and cleanup after the application

Note: Execution privilege on this package is granted by default to the execute_catalog_role. Consider granting the EXECUTE privilege only to specific users or roles because there might be operating system-specific limits on the maximum number of total locks available.

Functions and procedures included in the DBMS_LOCK package:


SQL> desc dbms_lock PROCEDURE ALLOCATE_UNIQUE Argument Name Type ---------------------------- ----------------------LOCKNAME VARCHAR2 LOCKHANDLE VARCHAR2 EXPIRATION_SECS NUMBER(38) FUNCTION CONVERT RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------ID NUMBER(38) LOCKMODE NUMBER(38) TIMEOUT NUMBER FUNCTION CONVERT RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------LOCKHANDLE VARCHAR2 LOCKMODE NUMBER(38) TIMEOUT NUMBER FUNCTION RELEASE RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------ID NUMBER(38) FUNCTION RELEASE RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------LOCKHANDLE VARCHAR2 FUNCTION REQUEST RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------ID NUMBER(38) LOCKMODE NUMBER(38) TIMEOUT NUMBER(38) RELEASE_ON_COMMIT BOOLEAN FUNCTION REQUEST RETURNS NUMBER(38) Argument Name Type ---------------------------- ----------------------LOCKHANDLE VARCHAR2 LOCKMODE NUMBER(38) TIMEOUT NUMBER(38) RELEASE_ON_COMMIT BOOLEAN PROCEDURE SLEEP Argument Name Type ---------------------------- ----------------------SECONDS NUMBER

In/Out Default? ------ -------IN OUT IN DEFAULT In/Out Default? ------ -------IN IN IN DEFAULT In/Out Default? ------ -------IN IN IN DEFAULT In/Out Default? ------ -------IN In/Out Default? ------ -------IN In/Out -----IN IN IN IN In/Out -----IN IN IN IN Default? -------DEFAULT DEFAULT DEFAULT Default? -------DEFAULT DEFAULT DEFAULT

In/Out Default? ------ -------IN

PROCEDURE ALLOCATE_UNIQUE: allocate a unique lock given a name. The procedure has three parameters: lockname: input parameter that is the name of the lock for which to generate unique lockid . The name can be up to 128 bytes, and it is case-sensitive. If this name already has been assigned a lockid, then return a handle to that lockid. Otherwise generate a new lockid and return a handle to it. lockhandle: output parameter that is not the actual lockid but a handle (up to 128 bytes). This handle can be used in subsequent calls to request, convert and release locks. A handle is used to reduce the chance that a programming error can accidentally create an incorrect but valid lockid. This will provide better isolation between different applications that are using this package. expiration_secs: output parameter containing the number of seconds after an 'allocate_unique' is most recently performed on this lock name, that this lock is subject to cleanup. The default value is 864000 sec (10 days).

dbms_lock_allocated

The information about name, lock id and expiration date can be found in the table.
SQL> desc dbms_lock_allocated Name Null? Type --------------------------------------- -------- -------------NAME LOCKID EXPIRATION NOT NULL VARCHAR2(128) NUMBER(38) DATE

All sessions invoking the allocate_unique procedure using the same name will refer to the same lock, but the lockhandle returned by the procedure could be different. The lockid's generated by allocate_unique are between 1073741824 and 1999999999 inclusive. Note: This procedure will always do a commit.

FUNCTION REQUEST: request a lock in a given mode. There are two request functions differening only in the first parameter which is either of the following: id: input parameter that is the lock id. The value may range from 0 to 1073741823. Numbers between 2000000000 to 2147483647 are reserved for products supplied by Oracle Corporation and should not be used. lockhandle: input parameter, is the handle originally returned by the allocate_unique procedure.

For either function the following parameters exist: lockmode: input parameter. There are five different lockmodes the default being exclusive mode. nl ss sx s ssx x NuLl Sub Shared Sub eXclusive Shared Shared Sub eXclusive eXclusive

This is the lock compatibility table:


held NL SS SX S SSX X get-> NL SUCC SUCC SUCC SUCC SUCC SUCC SS SUCC SUCC SUCC SUCC SUCC fail SX SUCC SUCC SUCC fail fail fail S SUCC SUCC fail SUCC fail fail SSX SUCC SUCC fail fail fail fail X SUCC fail fail fail fail fail

timeout: input parameter in seconds. If the lock cannot be acquired within this time period then the function returns a value of 1 (timeout see below). The default value is 32767 sec. release_on_commit: input parameter. The default value is FALSE meaning that the lock must be released explicitely with the release function or at the end of the session. If TRUE the lock will be released on commit or rollback.

The function returns a number. The possible values and meanings are as follows: 0 1 2 3 4 5 success timeout deadlock parameter error already own lock specified by 'id' or 'lockhandle' illegal lockhandle

FUNCTION CONVERT: convert lock from one mode to another. This function is overloaded on the first parameter as before. The parameters are: id: input parameter that is the lock id. The values admitted are from 0 to 1073741823 or lockhandle: input parameter, is the handle returned by the allocate_unique procedure.

For either function: lockmode: input parameter. See above the lockmode parameter of the request function timeout: input parameter in seconds. If the lock cannot be converted within this time period, the funcion returns the value 1 (timeout). The default value is 32767 sec.

The function returns a number. The possible values are the same of the request function (see above) FUNCTION RELEASE: release the lock previously acquired by the request function. This function is also overloaded. The parameters are: id: input parameter that is the lock id. The values admitted are from 0 to 1073741823 or lockhandle: input parameter, is the handle returned by the allocate_unique procedure.

The function returns a number. The possible values are: 0 success 3 parameter error 4 don't own lock specified by 'id' or 'lockhandle' 5 illegal lockhandle PROCEDURE SLEEP: Suspend the session for the specified period of time. The only input parameter is seconds. The maximum resolution is in hundreths of a second

The package specification also contains six constants that correspond to the six lock modes:
nl_mode ss_mode sx_mode s_mode ssx_mode x_mode constant constant constant constant constant constant integer integer integer integer integer integer := := := := := := 1; 2; 3; 4; 5; 6; => => => => => => NuLl Sub Shared Sub eXclusive Shared Shared Sub eXclusive eXclusive

Using the DBMS_LOCK package to synchronize sessions. Two or more sessions are required to perform some task at the same time. In our example we will use three sessions and the task will be to insert the time into the following table.
CREATE TABLE lock_test ( session_id number, action VARCHAR2(10), when date );

In the first session we now execute the following anonimous PL/SQL block to acquire an exclusive lock.
Set serveroutput on declare v_lockhandle varchar2(200); v_result number; begin dbms_lock.allocate_unique('sync_lock', v_lockhandle); v_result := dbms_lock.request(v_lockhandle, dbms_lock.x_mode); dbms_output.put_line( case when v_result=0 when v_result=1 when v_result=2 when v_result=3 when v_result=4 when v_result=5 end); end; /

then then then then then then

'Success' 'Timeout' 'Deadlock' 'Parameter Error' 'Already owned' 'Illegal Lock Handle'

The result should be Success.

Lets examine the dbms_lock_allocated table:

SQL> select * from

dbms_lock_allocated;

NAME LOCKID EXPIRATION -------------------- ---------- -------------------sync_lock 1073741853 19-OCT-2008 14:15:32

Now lets see what is in the dba_lock view or the dba_locks synonym.
SQL> desc dba_locks Name Null? -------------------------------------------- --------SESSION_ID LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2 LAST_CONVERT BLOCKING_OTHERS

Type -------------NUMBER VARCHAR2(26) VARCHAR2(40) VARCHAR2(40) VARCHAR2(40) VARCHAR2(40) NUMBER VARCHAR2(40)

SQL> select * from dba_locks;

SESSION_ID LOCK_TYPE

MODE_HELD

MODE_REQUE LOCK_ID1

LOCK_ID2

LAST_CONVERT BLOCKING_OTH

---------- ---------------- ---------- ---------- ---------- ---------- ------------ -----------164 XR 164 Control File 164 RS 166 PW 165 Redo Thread Null None 4 0 25 1 1 0 0 1 0 0 1456 Not Blocking 1450 Not Blocking 1447 Not Blocking 1441 Not Blocking 1447 Not Blocking 236 Not Blocking 1444 Not Blocking 1444 Not Blocking 1444 Not Blocking 1444 Not Blocking 1444 Not Blocking 1444 Not Blocking 1444 Not Blocking 1439 Not Blocking

Row-S (SS) None Row-S (SS) None Row-X (SX) None Exclusive None None None None None None None None None

121 PL/SQL User Lock Exclusive 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 163 Temp Segment Share Share Share Share Share Share Share

1073741853 0 1 2 3 4 5 6 201 3 0 0 0 0 0 0 0 1

Row-X (SX) None

14 rows selected.

Note: To create this view and synonym execute the catblock.sql script in $ORACLE_HOME/rdbms/admin directory.

In this example we are interested in the row, with SESSION_ID 121 and LOCK_ID1 1073741853 highlighted in red. We now start two additional sessions and execute the following PL/SQL in each session:
set serveroutput on declare v_result number; v_lockhandle varchar2(200); begin dbms_lock.allocate_unique('sync_lock', v_lockhandle); v_result := dbms_lock.request(v_lockhandle, dbms_lock.ss_mode); dbms_output.put_line( case when v_result=0 when v_result=1 when v_result=2 when v_result=3 when v_result=4 when v_result=5 end);

then then then then then then

'Success' 'Timeout' 'Deadlock' 'Parameter Error' 'Already owned' 'Illegal Lock Handle'

insert into lock_test values (SYS_CONTEXT ('USERENV', 'SID'),'started', sysdate); dbms_lock.sleep(5); insert into lock_test values (SYS_CONTEXT ('USERENV', 'SID'), 'ended' , sysdate); commit; end; /

Note: the function SYS_CONTEXT ('USERENV', 'SID') returns the SID of the session. In other words, the value of the column V$SESSION.SID. The code block requests a shared lock with the same name as was requested by the first session. This shared lock cannot be acquired if another session holds an exclusive lock (see above the compatibility chart). This means that the two sessions are blocked and will now wait until the owning session releases the lock.

Lets reexamine DBA_LOCKS to see the owner and waiters:


SQL> select * from dba_locks;

SESSION_ID LOCK_TYPE

MODE_HELD

MODE_REQUE LOCK_ID1

LOCK_ID2

LAST_CONVERT BLOCKING_OTH

---------- ---------------- ---------- ---------- ---------- ---------- ------------ -----------164 XR 164 Control File 164 RS 166 PW 165 Redo Thread Null None 4 0 25 1 1 0 0 1 0 0 1936 Not Blocking 1930 Not Blocking 1927 Not Blocking 1921 Not Blocking 1927 Not Blocking 716 Blocking 24 Not Blocking 18 Not Blocking 1924 Not Blocking 1924 Not Blocking 1924 Not Blocking 1924 Not Blocking 1924 Not Blocking 1924 Not Blocking 1924 Not Blocking 1919 Not Blocking

Row-S (SS) None Row-S (SS) None Row-X (SX) None Exclusive None None

121 PL/SQL User Lock Exclusive 153 PL/SQL User Lock None 133 PL/SQL User Lock None 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 163 Temp Segment Share Share Share Share Share Share Share

1073741853 0

Row-S (SS) 1073741853 0 Row-S (SS) 1073741853 0 None None None None None None None 1 2 3 4 5 6 201 3 0 0 0 0 0 0 0 1

Row-X (SX) None

16 rows selected.

We have two additional rows for SESSION_IDs 153 and 133, which are blocked trying to acquire a shared lock as seen in the MODE_REQUESTED column. Session 121 is now a blocking session! The DBA_WAITERS view gives us the same information. Session 121 is a holding session, with an exclusive lock. The other two sessions, 153 and 133, are waiting to acquire a shared lock.

SQL> select * from dba_waiters;

WAITING_SESSION HOLDING_SESSION LOCK_TYPE

MODE_HELD

MODE_REQUE

LOCK_ID1

LOCK_ID2

--------------- --------------- ---------------- ---------- ---------- ---------- ---------153 133 153 133 153 133 121 PL/SQL User Lock Exclusive 121 PL/SQL User Lock Exclusive 133 PL/SQL User Lock None 133 PL/SQL User Lock None 153 PL/SQL User Lock None 153 PL/SQL User Lock None Row-S (SS) 1073741853 Row-S (SS) 1073741853 Row-S (SS) 1073741853 Row-S (SS) 1073741853 Row-S (SS) 1073741853 Row-S (SS) 1073741853 0 0 0 0 0 0

6 rows selected.

Another way to see the same information is by executing the script utllockt.sql located in $ORACLE_HOME/rdbms/admin directory. The output looks as follows:
WAITING_SESSION LOCK_TYPE MODE_REQUE MODE_HELD LOCK_ID1 LOCK_ID2 ----------------- ----------------- ---------- ---------- ----------------- ----------------121 153 133 None PL/SQL User Lock PL/SQL User Lock Row-S (SS) Exclusive Row-S (SS) Exclusive 1073741853 1073741853 0 0

One remaining view for accessing lock information is V$LOCK.


SQL> desc v$lock Name Null? Type ----------------- ----------------------- ---------- --------------ADDR RAW(4) KADDR RAW(4) SID NUMBER TYPE VARCHAR2(2) ID1 NUMBER ID2 NUMBER LMODE NUMBER REQUEST NUMBER CTIME NUMBER BLOCK NUMBER

SQL> select * from v$lock;

ADDR

KADDR

SID TY

ID1

ID2

LMODE

REQUEST

CTIME

BLOCK

-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------33834228 3383423C 33834284 33834298 338342E0 338342F4 3383433C 33834350 338343F4 33834408 33834450 33834464 338344AC 338344C0 33834508 3383451C 33834564 33834578 338345C0 338345D4 3383461C 33834630 33834678 3383468C 338346D4 338346E8 33834730 33834744 3383478C 338347A0 33834844 33834858 165 XR 165 CF 167 PW 165 RS 166 RT 4 0 1 25 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 2 3 2 6 6 0 0 4 4 4 4 4 4 4 3 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 2760 2760 2746 2750 2750 264 253 250 2750 2750 2750 2750 2750 2750 2750 2741 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0

121 UL 1073741853 153 UL 1073741853 133 UL 1073741853 167 MR 167 MR 167 MR 167 MR 167 MR 167 MR 167 MR 164 TS 1 2 3 4 5 6 201 3

16 rows selected.

The column TYPE is the type of user or system lock. The value UL means User Lock. Lock information is also available in the Blocking Session page of Enterprise Manager.

Note: User locks never conflict with Oracle locks. Now lets return to the first session and release the exclusive lock using the following PL/SQL block.
declare v_lockhandle varchar2(200); v_result number; begin dbms_lock.allocate_unique('sync_lock', v_lockhandle); v_result := dbms_lock.release(v_lockhandle); dbms_output.put_line( case when v_result=0 when v_result=1 when v_result=2 when v_result=3 when v_result=4 when v_result=5 end); end;

then then then then then then

'Success' 'Timeout' 'Deadlock' 'Parameter Error' 'Already owned' 'Illegal Lock Handle'

Sessions 153 and 133 are now free and there are no more blocking sessions as can be seen in the output below. The two sessions do not block each other because they hold a lock which may be shared according to the lock compatibility chart shown earlier.
SQL> select * from dba_locks;

SESSION_ID LOCK_TYPE

MODE_HELD

MODE_REQUE LOCK_ID1

LOCK_ID2

LAST_CONVERT BLOCKING_OTH

---------- ---------------- ---------- ---------- ---------- ---------- ------------ -----------164 XR 164 Control File 164 RS 166 PW 165 Redo Thread Null None 4 0 25 1 1 0 0 1 0 0 3127 Not Blocking 3121 Not Blocking 3118 Not Blocking 3112 Not Blocking 3118 Not Blocking 41 Not Blocking 41 Not Blocking 3115 Not Blocking 3115 Not Blocking 3115 Not Blocking 3115 Not Blocking 3115 Not Blocking 3115 Not Blocking 3115 Not Blocking 3110 Not Blocking

Row-S (SS) None Row-S (SS) None Row-X (SX) None Exclusive None

153 PL/SQL User Lock Row-S (SS) None 133 PL/SQL User Lock Row-S (SS) None 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 166 Media Recovery 163 Temp Segment Share Share Share Share Share Share Share None None None None None None None

1073741853 0 1073741853 0 1 2 3 4 5 6 201 3 0 0 0 0 0 0 0 1

Row-X (SX) None

15 rows selected.

The two sessions require five seconds to finish because of the dbms_lock.sleep(5) procedure call used between the two insert statements. Note: The DBMS_LOCK.SLEEP procedure sleeps for the wrong period of time if the time interval is more then 1 hour. This is due to bug number 1842189. A workaround is to use the DBMS_BACKUP_RESTORE.SLEEP procedure which sleeps properly even if the time interval is more than 1 hour. Now lets see what the sessions have written into the lock_test table:
SQL> alter session set NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS'; SQL> SELECT * FROM lock_test; SESSION_ID ---------153 153 133 133 ACTION ---------started ended started ended WHEN -------------------09-OCT-2008 14:39:05 09-OCT-2008 14:39:10 09-OCT-2008 14:39:05 09-OCT-2008 14:39:10

Notice that both sessions start at the same time and therefore were exactly synchronised with the release of the lock by the lock holder!

An example of PL/SQL block to convert a lock mode is the following:


declare v_result number; begin v_result := dbms_lock.convert('1073741853', dbms_lock.sx_mode); dbms_output.put_line( case when v_result=0 then 'Success' when v_result=1 then 'Timeout' when v_result=2 then 'Deadlock' when v_result=3 then 'Parameter Error' when v_result=4 then 'Already owned' when v_result=5 then 'Illegal Lock Handle' end); end; /

You may choose any of the six lock modes. This was not used in our example but may be useful to change the mode of a lock owner so that for example other sessions may access the lock in certain modes but not others.

Conclusions In this article we have covered these subjects: An overview of the usage of the Oracle Supplied Package DBMS_LOCK. The explanation of all functions and procedures included in DBMS_LOCK package. Using DBMS_LOCK to synchronyze two or more sessions with an event or each other. Methods used to retrieve lock information using command line views and Enterprise manager.

S-ar putea să vă placă și