Einleitung Wie entsteht ein Deadlock? Wie protokolliert die
Transcrição
Einleitung Wie entsteht ein Deadlock? Wie protokolliert die
Betrifft Deadlock - Eine unangenehme Erfahrung Autor Peter Jensch ([email protected]) Art der Info Technische Background Info (Juli 2002) Quelle Aus unserer Schulungs- und Beratungstätigkeit Einleitung Das Auftreten von Deadlocks in Oracle-Datenbank-Anwendungen ist unangenehm. Schon bei gelegentlichem Auftreten ist ein sinnvolles Arbeiten mit der Anwendung nicht mehr möglich! Die Akzeptanz gegenüber der Applikation ist somit nicht gegeben, zumal die Deadlocks häufig ein Problem der Applikation sind. Die Vermeidung bzw. das Auffinden und die korrekte Interpretation von Deadlocks ist daher ein sehr wichtiges Thema für den Anwendungsentwickler und einen DBA. Besonders anfällig für Deadlocks sind alle Tabellen mit expliziten Locks, um die Schreib- / Lese-Konsistenz zu gewährleisten, nicht indizierte FK-Constraints (nicht mehr bei OracleVersion 9i) und unzureichende Storage - Parameter von Datensegmenten (im Wesentlichen sind dies die Parameter INITTRANS, MAXTRANS und PCTFRE) können Ursachen für ungewollte Locks und Deadlocks sein. Die Ursachen sind vielschichtig und müssen von Fall zu Fall neu untersucht werden. Wie entsteht ein Deadlock? Versuchen zwei oder mehr User auf dieselben Datenquellen in unterschiedlicher Reihenfolge zuzugreifen, kommt es zum (Dead-)Lock der Ressourcen. Hierbei kann der jeweilige Lock auf Tabellen-, Partitions- oder Record-Level erfolgen. Die Liste der möglichen Locks ist gross. Ich möchte an dieser Stelle lediglich die wesentlichen Locks kurz aufführen. DX TM TX UL ... Distributed TX DML-Enqueue Transaction User-defined Locks Wie protokolliert die Datenbank einen Deadlock? Beim Deadlock (Oracle-Fehlermeldung: ora-00060) wird eine entsprechende Meldung ins Alert-Log geschrieben. Zusätzlich wird im USER_DUMP_DESTINATION ein Trace-File erzeugt. Der Eintrag im Alert-Log-File gibt einen Hinweis auf die File-Bezeichnung des Trace-Files. Den Alert-Log-File und den Trace-File zu analysieren ist sehr umfangreich und aufwendig. Oft wird man erst durch mühsames Suchen fündig – wenn überhaupt. Eine schnelle und detaillierte Problemlösung ist daher nur bedingt möglich. Beispiel aus einer alert_log-Datei: Mon Jul 29 09:14:42 2002 ORA-000060: Deadlock detected. More info in file d:\oracle\admin\DB1\udump\ORA01784.TRC. Beispiel (Auszug) aus dem zugehörigen Trace-File ORA01784.TRC: Dump file d:\oracle\admin\DB1\udump\ORA01784.TRC Mon Jul 29 09:14:42 2002 ORACLE V9.0.1.2.1 - Production vsnsta=0 vsnsql=10 vsnxtr=3 Windows 2000 Version 5.0 Service Pack 2, CPU type 586 Oracle9i Enterprise Edition Release 9.0.1.2.1 - Production With the Partitioning option JServer Release 9.0.1.2.0 - Production Windows 2000 Version 5.0 Service Pack 2, CPU type 586 Instance name: db1 Redo thread mounted by this instance: 1 Oracle process number: 12 Windows thread id: 1784, image: ORACLE.EXE *** 2002-07-29 09:14:42.000 *** SESSION ID:(12.11) 2002-07-29 09:14:42.000 DEADLOCK DETECTED Current SQL statement for this session: update emp set sal = sal + 100 where deptno = 20 The following deadlock is not an ORACLE error. It is a deadlock due to user error in the design of an application or from issuing incorrect ad-hoc SQL. The following information may aid in determining the deadlock: Deadlock graph: ---------Blocker(s)-------- ---------Waiter(s)--------Resource Name process session holds waits process session holds waits TX-00080006-000008f8 12 12 X 11 10 X TX-00090002-000008fc 11 10 X 12 12 X session 12: DID 0001-000C-00000002 session 10: DID 0001-000B-00000002 session 10: DID 0001-000B-00000002 session 12: DID 0001-000C-00000002 Rows waited on: Session 10: obj - rowid = 00007C6A - AAAHxqAABAAAO/CAAG (dictionary objn - 31850, file - 1, block - 61378, slot - 6) Session 12: obj - rowid = 00007C6A - AAAHxqAABAAAO/CAAA (dictionary objn - 31850, file - 1, block - 61378, slot - 0) SQL statements executed by the waiting sessions: Session 10: update emp set sal = sal + 100 where deptno = 10 =================================================== PROCESS STATE ------------Process global information: process: 671C58C8, call: 67247E80, xact: 6689333C, curses: 671EC654, usrses: 671EC654 ---------------------------------------SO: 671C58C8, type: 2, owner: 00000000, flag: INIT/-/-/0x00 (process) Oracle pid=12, calls cur/top: 67247E80/67247E80, flag: (0) int error: 0, call error: 0, sess error: 0, txn error 0 (post info) last post received: 120 0 4 last post received-location: kslpsr last process to post me: 671c3e28 1 6 last post sent: 0 0 15 last post sent-location: ksasnd last process posted by me: 671c3e28 1 6 (latch info) wait_event=0 bits=10 holding 19792c0 Parent+children enqueue hash chains level=4 Location from where latch is held: ksqcmi: kslgpl: Sind diese Informationen nicht ausreichend, so kann über Datenbank-Events zusätzliche Information erzeugt werden. Durch Hinzufügen des folgenden Init.ora-Parameters: Event=“60 trace name errorstack level 3;name systemstate level 10“ Hierbei werden ggf. sehr grosse Trace-Files erzeugt. Die Grösse der Trace-Files wird über den Init.ora-Parameter max_dump_file_size begrenzt. Einträge, die über diese FileBegrenzung hinausgehen, werden abgeschnitten – ggf. ist die Interpretation deutlich eingeschränkt, da wesentliche Informationen abgeschnitten wurden. Lösungsansatz: Unter den Trivadis-Scripten, die Sie unter www.trivadis.com kostenlos herunterladen können, findet man zwei Scripte (sslckwa1.sql und sslckwai.sql), die zum Ausführungszeitpunkt alle gelockten Tabellen und weitere Infos rund um den „Locker“, „Waiter“ und der „Herkunft“ ausgeben. Die Ausführung der Scripte ist auf den aktuellen Moment bezogen, d.h. auftretende Deadlocks werden nicht protokolliert. Nachfolgend wird ein Datenbank-Trigger und eine Datenbank-Procedure vorgestellt, die beim Auftreten eine Protokollierung der Informationen in Log-Tabellen vornehmen. Hierbei wird beim Auftreten des ORA-00060 der Datenbank-Trigger aktiviert und die wesentlichen Informationen des Deadlocks werden in die beiden Tabellen eingefügt. Nachfolgend werden Ihnen die notwendige Tabellen, der Datenbank-Trigger und die PL/SQL-Procedure vorgestellt, die unter dem User Sys installiert werden: Tabelle tvd_lock_info_1 CREATE TABLE tvd_lock_info_1 ( lock_date DATE , os_locker VARCHAR2(30) , locker_schema VARCHAR2(30) , locker_pid VARCHAR2(9) , os_waiter VARCHAR2(30) , waiter_schema VARCHAR2(30) , waiter_pid VARCHAR2(9) , sql_text_waiter VARCHAR2(200) ); Tabelle tvd_lock_info_2 CREATE TABLE tvd_lock_info_2 ( lock_date DATE , wait VARCHAR2(3) , os_user VARCHAR2(30) , process VARCHAR2(9) , locker VARCHAR2(30) , object_owner VARCHAR2(30) , object_name VARCHAR2(30) , program varchar2(50) ) TABLESPACE users / Procedure tvd_prc_lock_info CREATE OR REPLACE PROCEDURE tvd_prc_lock_info IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO tvd_lock_info_1 ( lock_date, os_locker, locker_schema, locker_pid, os_waiter, waiter_schema, waiter_pid, sql_text_waiter ) SELECT /* first the table-level locks (TM) and mixed TM/TX TX/TM */ SYSDATE , S_LOCKER.OSUSER OS_LOCKER , S_LOCKER.USERNAME LOCKER_SCHEMA , S_LOCKER.PROCESS LOCKER_PID , S_WAITER.OSUSER OS_WAITER , S_WAITER.USERNAME WAITER_SCHEMA , S_WAITER.PROCESS WAITER_PID , 'Table lock (TM): ' || U.NAME || '.' || O.NAME || ' - Mode held: '|| DECODE(L_LOCKER.LMODE, 0, 'None', /* same as Monitor */ 1, 'Null', /* N */ 2, 'Row-S (SS)', /* L */ 3, 'Row-X (SX)', /* R */ 4, 'Share', /* S */ 5, 'S/Row-X (SSX)', /* C */ 6, 'Exclusive', /* X */ '???: ' || to_char(L_LOCKER.LMODE)) || ' / Mode requested: '|| DECODE(L_WAITER.REQUEST, 0, 'None', /* same as Monitor */ 1, 'Null', /* N */ 2, 'Row-S (SS)', /* L */ 3, 'Row-X (SX)', /* R */ 4, 'Share', /* S */ 5, 'S/Row-X (SSX)', /* C */ 6, 'Exclusive', /* X */ '???: '||to_char(L_WAITER.REQUEST)) SQL_TEXT_WAITER FROM OBJ$ O , USER$ U , V$LOCK L_WAITER , V$LOCK L_LOCKER , V$SESSION S_WAITER , V$SESSION S_LOCKER WHERE S_WAITER.SID = L_WAITER.SID AND L_WAITER.TYPE IN ('TM') AND S_LOCKER.sid = L_LOCKER.sid AND L_LOCKER.ID1 = L_WAITER.ID1 AND L_WAITER.REQUEST > 0 AND L_LOCKER.LMODE > 0 AND L_WAITER.ADDR != L_LOCKER.ADDR AND L_WAITER.ID1 = O.OBJ# AND U.USER# = O.OWNER# UNION SELECT /* now the (usual) row-locks TX */ SYSDATE , S_LOCKER.OSUSER OS_LOCKER , S_LOCKER.USERNAME LOCKER_SCHEMA , S_LOCKER.PROCESS LOCK_PID , S_WAITER.OSUSER OS_WAITER , S_WAITER.USERNAME WAITER_SCHEMA , S_WAITER.PROCESS WAITER_PID , 'TX: ' || O.SQL_TEXT SQL_TEXT_WAITER FROM V$LOCK L_WAITER , V$LOCK L_LOCKER , V$SESSION S_WAITER , V$SESSION S_LOCKER , V$_LOCK1 L1_WAITER , V$OPEN_CURSOR O WHERE S_WAITER.SID = L_WAITER.SID AND L_WAITER.TYPE IN ('TX') AND S_LOCKER.sid = L_LOCKER.sid AND L_LOCKER.ID1 = L_WAITER.ID1 AND L_WAITER.REQUEST > 0 AND L_LOCKER.LMODE > 0 AND L_WAITER.ADDR != L_LOCKER.ADDR AND L1_WAITER.LADDR = L_WAITER.ADDR AND L1_WAITER.KADDR = L_WAITER.KADDR AND L1_WAITER.SADDR = O.SADDR; INSERT INTO tvd_lock_info_2 ( lock_date, wait, os_user, process, locker, object_owner, object_name, program ) SELECT SYSDATE , DECODE(L.REQUEST,0,'NO','YES') WAIT , S.OSUSER , S.PROCESS , S.USERNAME LOCKER , U.NAME T_OWNER , O.NAME OBJECT_NAME , ' '||S.PROGRAM PROGRAM FROM V$LOCK L , USER$ U , OBJ$ O , V$SESSION S WHERE U.USER# = O.OWNER# AND S.SID = L.SID AND L.ID1 = O.OBJ# AND L.TYPE = 'TM' AND U.NAME != 'SYS' UNION SELECT SYSDATE , DECODE(L.REQUEST,0,'NO','YES') WAIT , S.OSUSER , S.PROCESS , S.USERNAME LOCKER , '-' , 'Record(s)' , ' '||S.PROGRAM PROGRAM FROM V$LOCK L , V$SESSION S WHERE S.SID = L.SID AND L.TYPE = 'TX'; COMMIT; END; / show errors Trigger log_deadlock_errors CREATE OR REPLACE TRIGGER log_deadlock_errors AFTER SERVERERROR ON DATABASE BEGIN IF (IS_SERVERERROR (60)) THEN sys.tvd_prc_lock_info; END IF; END log_deadlock_errors; / show errors Test des neuen Triggers Anhand eines kleinen Testfalles kann ein Deadlocks erzeugt werden: Verbinden Sie sich mit zwei SQL-Plus Sessions als User Scott auf der Datenbank, nachfolgend werden diese Sessions als Session A und Session B bezeichnen. Um den Deadlock zu provozieren, muss in beiden Sessions ein Lock auf eine Tabelle ausgeführt werden. Werden die beiden Befehle zu den beiden Sessions hin vertauscht, so entsteht der Deadlock: Sequenz 1 2 Session A Session B UPDATE emp SET sal = sal + 100 WHERE deptno = 10; UPDATE emp SET sal = sal + 200 WHERE deptno = 20; 3 UPDATE emp SET sal = sal + 200 WHERE deptno = 20; 4 UPDATE emp SET sal = sal + 100 WHERE deptno = 10; 5 UPDATE emp SET sal = sal + 200 WHERE deptno = 20 * ERROR at line 1: ORA-00060: deadlock detected while waiting for resource Auch wenn die beiden Scott-Sessions wieder beendet werden, lassen sich die protokollierten Informationen zu einem späteren Zeitpunkt über den User SYS aus den beiden Tabellen auswerten: SELECT , , , FROM to_char(lock_date,'HH24:MI:SS') lock_date locker_schema LCK waiter_schema WAI sql_text_waiter tvd_lock_info_1; LOCK_DAT -------09:14:42 09:14:42 09:14:42 09:14:42 09:14:42 09:14:42 09:14:42 LCK ----------SCOTT SCOTT SCOTT SCOTT SCOTT SCOTT SCOTT WAI ----------SCOTT SCOTT SCOTT SCOTT SCOTT SCOTT SCOTT SQL_TEXT_WAITER ---------------------------------------------------------------TX: SELECT ATTRIBUTE FROM V$CONTEXT WHERE NAMESPACE = 'LBAC$L TX: SELECT MAX(TAG#) FROM LBAC$LAB TX: SELECT POL# FROM LBAC$POL ORDER BY POL# TX: SELECT POL#,PACKAGE FROM LBAC$POL WHERE BITAND(FLAGS,1) = TX: select pol#, usr_name, usr_labels, package, privs from lbac$ TX: table_1_0_139_0_0_0 TX: update emp set sal = sal + 100 where deptno = 10 Bereits durch Ausführen eines Select-Befehls auf die tvd_lock_info_1-Tabelle lässt sich feststellen WER - WAS - WANN gelockt hat. Durch den weiteren Select-Befehl auf die Tabelle tvd_lock_info_2-Tabelle lassen sich weitere Informationen bestimmen: Welche weiteren Sessions, Tabellen und Programme sind betroffen. SELECT to_char(lock_date,'HH24:MI:SS') lock_date, wait, os_user, locker, program, object_owner, object_name FROM tvd_lock_info_2; LOCK_DAT -------09:14:43 09:14:43 09:14:43 09:14:43 09:14:43 WAI --NO NO NO NO YES OS_USER -------------TRIVADIS\pej TRIVADIS\pej TRIVADIS\pej TRIVADIS\pej TRIVADIS\pej LOCKER ------SCOTT SCOTT SCOTT SCOTT SCOTT PROGRAM -----------sqlplusw.exe sqlplusw.exe sqlplusw.exe sqlplusw.exe sqlplusw.exe OBJECT_OWNER ---------------SCOTT SCOTT - OBJECT_NAME -------------Record(s) EMP Record(s) EMP Record(s) Eine Datei mit den notwendigen Create-Table, Create-Procedure und dem Database-Trigger (cslcktrg.sql) und zur formatierten Ausgabe (lslcksta.sql) finden Sie auf unserer Website im Bereich Tools und Services zum kostenlosen herunterladen. Weitere Informationen über die Themen Transaktionsverhalten, Locks und Deadlocks finden Sie in früheren Ausgaben der DOAG, auf unserer Website und in unseren OracleSchulungen! Viel Spass mit Oracle und keine Applikationsabbrüche und Wartezeiten durch Deadlocks! Peter Jensch Trivadis AG Peter Jensch Max-Lang-Strasse 56 D-70771 Leinfelden-Echterdingen Internet: http://www.trivadis.com Mail: [email protected] Tel: +49 711 90 36 32 30 Fax: +49 711 90 36 32 59