02.06.2013 Views

Introduction Comment survient un Deadlock? Comment ... - Trivadis

Introduction Comment survient un Deadlock? Comment ... - Trivadis

Introduction Comment survient un Deadlock? Comment ... - Trivadis

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Objet <strong>Deadlock</strong> - Une expérience désagréable<br />

Auteur Peter Jensch (peter.jensch@trivadis.com)<br />

Nature des informations Informations à caractère technique<br />

Source Notre expérience de formation et de conseil<br />

<strong>Introduction</strong><br />

L'apparition de <strong>Deadlock</strong>s dans les applications de base de données Oracle est toujours<br />

<strong>un</strong>e expérience désagréable. Même occasionnel, <strong>un</strong> <strong>Deadlock</strong> (interblocage) empêche<br />

toute utilisation pertinente de l'application en cours! L'acceptation de l'application est alors<br />

refusée, d'autant que les <strong>Deadlock</strong>s révèlent généralement <strong>un</strong> problème issu de<br />

l'application. Il est donc essentiel pour les développeurs d'applications et les DBA de<br />

savoir éviter les <strong>Deadlock</strong>s ou de savoir les dénicher et les interpréter correctement.<br />

Sont particulièrement sensibles aux <strong>Deadlock</strong>s toutes les tables contenant des Locks<br />

explicites assurant la cohérence écriture/lecture, toutes les contraintes FK non indexées<br />

(n'existent plus dans la version 9i d'Oracle) et tous les paramètres de stockage des<br />

segments de données dont la valeur est insuffisante (en l'occurrence, les paramètres<br />

INITTRANS, MAXTRANS et PCTFREE). Ces éléments peuvent être à l'origine de blocages et<br />

d'interblocages intempestifs. Les causes sont multiples et doivent être recherchées au cas<br />

par cas.<br />

<strong>Comment</strong> <strong>survient</strong> <strong>un</strong> <strong>Deadlock</strong>?<br />

Lorsque deux utilisateurs (ou plus) tentent d'accéder aux mêmes sources de données dans<br />

<strong>un</strong> ordre différent, il se produit <strong>un</strong> blocage ou <strong>un</strong> interblocage des ressources qui peut se<br />

manifester au niveau des tables, des partitions ou des enregistrements.<br />

La liste des blocages possibles est très longue. Je me limiterai donc à la présentation des<br />

blocages principaux.<br />

DX Distributed TX<br />

TM DML-Enqueue<br />

TX Transaction<br />

UL User-defined Locks<br />

...<br />

<strong>Comment</strong> <strong>un</strong>e base de données consigne-t-elle <strong>un</strong> <strong>Deadlock</strong>?<br />

Lorsqu'<strong>un</strong> <strong>Deadlock</strong> se produit (message d'erreur Oracle: ora-00060), <strong>un</strong> message s'inscrit<br />

dans le journal d'alerte et <strong>un</strong> fichier de trace est créé dans le dossier


USER_DUMP_DESTINATION. L'entrée inscrite dans le journal d'alerte fournit le nom du<br />

fichier de trace créé.<br />

Analyser le journal d'alerte et le fichier de trace est <strong>un</strong>e opération fastidieuse et exigeante.<br />

Trouver la solution au problème relève alors du miracle. Sous certaines conditions, il est<br />

pourtant possible de trouver <strong>un</strong>e réponse rapide et détaillée.<br />

Exemple tiré d'<strong>un</strong> fichier alert_log:<br />

Mon Jul 29 09:14:42 2002<br />

ORA-000060: <strong>Deadlock</strong> detected. More info in file<br />

d:\oracle\admin\DB1\udump\ORA01784.TRC.<br />

Exemple (extrait) tiré du fichier de trace correspondant ORA01784.TRC:<br />

Dump file d:\oracle\admin\DB1\udump\ORA01784.TRC<br />

Mon Jul 29 09:14:42 2002<br />

ORACLE V9.0.1.2.1 - Production vsnsta=0<br />

vsnsql=10 vsnxtr=3<br />

Windows 2000 Version 5.0 Service Pack 2, CPU type 586<br />

Oracle9i Enterprise Edition Release 9.0.1.2.1 - Production<br />

With the Partitioning option<br />

JServer Release 9.0.1.2.0 - Production<br />

Windows 2000 Version 5.0 Service Pack 2, CPU type 586<br />

Instance name: db1<br />

Redo thread mo<strong>un</strong>ted by this instance: 1<br />

Oracle process number: 12<br />

Windows thread id: 1784, image: ORACLE.EXE<br />

*** 2002-07-29 09:14:42.000<br />

*** SESSION ID:(12.11) 2002-07-29 09:14:42.000<br />

DEADLOCK DETECTED<br />

Current SQL statement for this session:<br />

update emp set sal = sal + 100<br />

where deptno = 20<br />

The following deadlock is not an ORACLE error. It is a<br />

deadlock due to user error in the design of an application<br />

or from issuing incorrect ad-hoc SQL. The following<br />

information may aid in determining the deadlock:<br />

<strong>Deadlock</strong> graph:<br />

---------Blocker(s)-------- ---------Waiter(s)---------<br />

Resource Name process session holds waits process session holds waits<br />

TX-00080006-000008f8 12 12 X 11 10 X<br />

TX-00090002-000008fc 11 10 X 12 12 X<br />

session 12: DID 0001-000C-00000002 session 10: DID 0001-000B-00000002<br />

session 10: DID 0001-000B-00000002 session 12: DID 0001-000C-00000002<br />

Rows waited on:<br />

Session 10: obj - rowid = 00007C6A - AAAHxqAABAAAO/CAAG<br />

(dictionary objn - 31850, file - 1, block - 61378, slot - 6)<br />

Session 12: obj - rowid = 00007C6A - AAAHxqAABAAAO/CAAA<br />

(dictionary objn - 31850, file - 1, block - 61378, slot - 0)<br />

SQL statements executed by the waiting sessions:<br />

Session 10:<br />

update emp set sal = sal + 100<br />

where deptno = 10<br />

===================================================<br />

PROCESS STATE<br />

-------------<br />

Process global information:


process: 671C58C8, call: 67247E80, xact: 6689333C, curses: 671EC654, usrses:<br />

671EC654<br />

----------------------------------------<br />

SO: 671C58C8, type: 2, owner: 00000000, flag: INIT/-/-/0x00<br />

(process) Oracle pid=12, calls cur/top: 67247E80/67247E80, flag: (0) -<br />

int error: 0, call error: 0, sess error: 0, txn error 0<br />

(post info) last post received: 120 0 4<br />

last post received-location: kslpsr<br />

last process to post me: 671c3e28 1 6<br />

last post sent: 0 0 15<br />

last post sent-location: ksasnd<br />

last process posted by me: 671c3e28 1 6<br />

(latch info) wait_event=0 bits=10<br />

holding 19792c0 Parent+children enqueue hash chains level=4<br />

Location from where latch is held: ksqcmi: kslgpl:<br />

Si ces informations ne suffisent pas, il est possible de générer des informations<br />

complémentaires par l'intermédiaire des événements de base de données en ajoutant le<br />

paramètre Init.ora suivant:<br />

Event=“60 trace name errorstack level 3;name systemstate level 10“<br />

Ce paramètre peut, le cas échéant, produire de très gros fichiers de trace. La taille de ces<br />

fichiers est définie par le paramètre Init.ora nommé max_dump_file_size. Les entrées<br />

inscrites au-delà de cette limite sont tout simplement sectionnées: l'interprétation du fichier<br />

est alors considérablement plus difficile puisque certaines informations capitales ont été<br />

coupées.<br />

Solution au problème:<br />

Parmi les scripts <strong>Trivadis</strong> que vous pouvez télécharger gratuitement à l'adresse<br />

www.trivadis.com, vous trouverez deux scripts (sslckwa1.sql et sslckwai.sql) qui, au<br />

moment de leur exécution, éditent toutes les tables actuellement bloquées et fournissent<br />

diverses informations concernant le Locker, le Waiter et l'origine. L'exécution des scripts se<br />

réfère au moment présent, si bien que les <strong>Deadlock</strong>s en cours ne sont pas consignés.<br />

Heureusement, il existe <strong>un</strong> trigger et <strong>un</strong>e procédure de base de données capables<br />

d'exécuter l'enregistrement des informations dans les tables Log dès que <strong>survient</strong> <strong>un</strong><br />

<strong>Deadlock</strong>. Le fonctionnement est le suivant: lorsque <strong>survient</strong> le message Oracle ORA-<br />

00060, le trigger de base de données est activé et les informations importantes du<br />

<strong>Deadlock</strong> sont insérées dans deux tables.


Voici les tables requises, le trigger de base de données et la procédure PL/SQL installés<br />

sous User Sys:<br />

Table tvd_lock_info_1<br />

CREATE TABLE tvd_lock_info_1 (<br />

lock_date DATE<br />

, os_locker VARCHAR2(30)<br />

, locker_schema VARCHAR2(30)<br />

, locker_pid VARCHAR2(9)<br />

, os_waiter VARCHAR2(30)<br />

, waiter_schema VARCHAR2(30)<br />

, waiter_pid VARCHAR2(9)<br />

, sql_text_waiter VARCHAR2(200)<br />

);<br />

Table tvd_lock_info_2<br />

CREATE TABLE tvd_lock_info_2 (<br />

lock_date DATE<br />

, wait VARCHAR2(3)<br />

, os_user VARCHAR2(30)<br />

, process VARCHAR2(9)<br />

, locker VARCHAR2(30)<br />

, object_owner VARCHAR2(30)<br />

, object_name VARCHAR2(30)<br />

, program varchar2(50)<br />

)<br />

TABLESPACE users<br />

/<br />

Procédure tvd_prc_lock_info<br />

CREATE OR REPLACE PROCEDURE tvd_prc_lock_info<br />

IS<br />

PRAGMA AUTONOMOUS_TRANSACTION;<br />

BEGIN<br />

INSERT INTO tvd_lock_info_1<br />

( lock_date, os_locker, locker_schema, locker_pid,<br />

os_waiter, waiter_schema, waiter_pid, sql_text_waiter )<br />

SELECT /* first the table-level locks (TM) and mixed TM/TX TX/TM */<br />

SYSDATE<br />

, S_LOCKER.OSUSER OS_LOCKER<br />

, S_LOCKER.USERNAME LOCKER_SCHEMA<br />

, S_LOCKER.PROCESS LOCKER_PID<br />

, S_WAITER.OSUSER OS_WAITER<br />

, S_WAITER.USERNAME WAITER_SCHEMA<br />

, S_WAITER.PROCESS WAITER_PID<br />

, 'Table lock (TM): ' || U.NAME || '.' || O.NAME || ' - Mode held: '||<br />

DECODE(L_LOCKER.LMODE,<br />

0, 'None', /* same as Monitor */<br />

1, 'Null', /* N */<br />

2, 'Row-S (SS)', /* L */<br />

3, 'Row-X (SX)', /* R */<br />

4, 'Share', /* S */<br />

5, 'S/Row-X (SSX)', /* C */<br />

6, 'Exclusive', /* X */<br />

'???: ' || to_char(L_LOCKER.LMODE)) || ' / Mode requested: '||


FROM<br />

DECODE(L_WAITER.REQUEST,<br />

0, 'None', /* same as Monitor */<br />

1, 'Null', /* N */<br />

2, 'Row-S (SS)', /* L */<br />

3, 'Row-X (SX)', /* R */<br />

4, 'Share', /* S */<br />

5, 'S/Row-X (SSX)', /* C */<br />

6, 'Exclusive', /* X */<br />

'???: '||to_char(L_WAITER.REQUEST))<br />

SQL_TEXT_WAITER<br />

OBJ$ O<br />

, USER$ U<br />

, V$LOCK L_WAITER<br />

, V$LOCK L_LOCKER<br />

, V$SESSION S_WAITER<br />

, V$SESSION S_LOCKER<br />

WHERE S_WAITER.SID = L_WAITER.SID<br />

AND L_WAITER.TYPE IN ('TM')<br />

AND S_LOCKER.sid = L_LOCKER.sid<br />

AND L_LOCKER.ID1 = L_WAITER.ID1<br />

AND L_WAITER.REQUEST > 0<br />

AND L_LOCKER.LMODE > 0<br />

AND L_WAITER.ADDR != L_LOCKER.ADDR<br />

AND L_WAITER.ID1 = O.OBJ#<br />

AND U.USER# = O.OWNER#<br />

UNION<br />

SELECT /* now the (usual) row-locks TX */<br />

SYSDATE<br />

, S_LOCKER.OSUSER OS_LOCKER<br />

, S_LOCKER.USERNAME LOCKER_SCHEMA<br />

, S_LOCKER.PROCESS LOCK_PID<br />

, S_WAITER.OSUSER OS_WAITER<br />

, S_WAITER.USERNAME WAITER_SCHEMA<br />

, S_WAITER.PROCESS WAITER_PID<br />

, 'TX: ' || O.SQL_TEXT SQL_TEXT_WAITER<br />

FROM<br />

V$LOCK L_WAITER<br />

, V$LOCK L_LOCKER<br />

, V$SESSION S_WAITER<br />

, V$SESSION S_LOCKER<br />

, V$_LOCK1 L1_WAITER<br />

, V$OPEN_CURSOR O<br />

WHERE S_WAITER.SID = L_WAITER.SID<br />

AND L_WAITER.TYPE IN ('TX')<br />

AND S_LOCKER.sid = L_LOCKER.sid<br />

AND L_LOCKER.ID1 = L_WAITER.ID1<br />

AND L_WAITER.REQUEST > 0<br />

AND L_LOCKER.LMODE > 0<br />

AND L_WAITER.ADDR != L_LOCKER.ADDR<br />

AND L1_WAITER.LADDR = L_WAITER.ADDR<br />

AND L1_WAITER.KADDR = L_WAITER.KADDR<br />

AND L1_WAITER.SADDR = O.SADDR;<br />

INSERT INTO tvd_lock_info_2<br />

( lock_date, wait, os_user, process, locker,<br />

object_owner, object_name, program )<br />

SELECT SYSDATE<br />

, DECODE(L.REQUEST,0,'NO','YES') WAIT<br />

, S.OSUSER<br />

, S.PROCESS<br />

, S.USERNAME LOCKER<br />

, U.NAME T_OWNER<br />

, O.NAME OBJECT_NAME<br />

, ' '||S.PROGRAM PROGRAM<br />

FROM<br />

V$LOCK L<br />

, USER$ U<br />

, OBJ$ O


, V$SESSION S<br />

WHERE U.USER# = O.OWNER#<br />

AND S.SID = L.SID<br />

AND L.ID1 = O.OBJ#<br />

AND L.TYPE = 'TM'<br />

AND U.NAME != 'SYS'<br />

UNION<br />

SELECT SYSDATE<br />

, DECODE(L.REQUEST,0,'NO','YES') WAIT<br />

, S.OSUSER<br />

, S.PROCESS<br />

, S.USERNAME LOCKER<br />

, '-'<br />

, 'Record(s)'<br />

, ' '||S.PROGRAM PROGRAM<br />

FROM V$LOCK L<br />

, V$SESSION S<br />

WHERE S.SID = L.SID<br />

AND L.TYPE = 'TX';<br />

COMMIT;<br />

END;<br />

/<br />

show errors<br />

Trigger log_deadlock_errors<br />

CREATE OR REPLACE TRIGGER log_deadlock_errors<br />

AFTER SERVERERROR<br />

ON DATABASE<br />

BEGIN<br />

IF (IS_SERVERERROR (60)) THEN<br />

sys.tvd_prc_lock_info;<br />

END IF;<br />

END log_deadlock_errors;<br />

/<br />

show errors<br />

Test du nouveau trigger<br />

Il est possible de provoquer <strong>un</strong> <strong>Deadlock</strong> à l'aide d'<strong>un</strong> petit test:<br />

Connectez-vous à <strong>un</strong>e base de données en ouvrant deux sessions SQL-Plus en tant que<br />

l'utilisateur Scott; nous appellerons ces deux sessions Session A et Session B. Pour<br />

provoquer le <strong>Deadlock</strong>, il faut exécuter <strong>un</strong> Lock sur <strong>un</strong>e table dans les deux sessions. Si les<br />

deux commandes des deux sessions sont interverties, <strong>un</strong> <strong>Deadlock</strong> se produit:<br />

Séquence Session A Session B<br />

1 UPDATE emp SET sal = sal +<br />

100<br />

WHERE deptno = 10;<br />

2 UPDATE emp SET sal = sal + 200<br />

WHERE deptno = 20;<br />

3 UPDATE emp SET sal = sal +<br />

200<br />

WHERE deptno = 20;


4 UPDATE emp SET sal = sal + 100<br />

WHERE deptno = 10;<br />

5 UPDATE emp SET sal = sal +<br />

200<br />

WHERE deptno = 20<br />

*<br />

ERROR at line 1:<br />

ORA-00060: deadlock detected<br />

while waiting for resource<br />

Même après la fermeture des deux sessions Scott, les informations enregistrées peuvent<br />

être analysées à partir des deux tables via User SYS:<br />

SELECT to_char(lock_date,'HH24:MI:SS') lock_date<br />

, locker_schema LCK<br />

, waiter_schema WAI<br />

, sql_text_waiter<br />

FROM tvd_lock_info_1;<br />

LOCK_DAT LCK WAI SQL_TEXT_WAITER<br />

-------- ----------- ----------- ------------------------------------------------------<br />

----------<br />

09:14:42 SCOTT SCOTT TX: SELECT ATTRIBUTE FROM V$CONTEXT WHERE NAMESPACE<br />

= 'LBAC$L<br />

09:14:42 SCOTT SCOTT TX: SELECT MAX(TAG#) FROM LBAC$LAB<br />

09:14:42 SCOTT SCOTT TX: SELECT POL# FROM LBAC$POL ORDER BY POL#<br />

09:14:42 SCOTT SCOTT TX: SELECT POL#,PACKAGE FROM LBAC$POL WHERE<br />

BITAND(FLAGS,1) =<br />

09:14:42 SCOTT SCOTT TX: select pol#, usr_name, usr_labels, package, privs<br />

from lbac$<br />

09:14:42 SCOTT SCOTT TX: table_1_0_139_0_0_0<br />

09:14:42 SCOTT SCOTT TX: update emp set sal = sal + 100 where deptno = 10<br />

L'exécution d'<strong>un</strong> simple Select dans la table tvd_lock_info_1 permet déjà de savoir QUI a<br />

bloqué QUOI et COMMENT. Un autre Select dans la table tvd_lock_info_2 permet<br />

d'obtenir des informations complémentaires: quels sont les autres sessions, tables et<br />

programmes touchés?<br />

SELECT to_char(lock_date,'HH24:MI:SS') lock_date, wait, os_user, locker,<br />

program, object_owner, object_name<br />

FROM tvd_lock_info_2;<br />

LOCK_DAT WAI OS_USER LOCKER PROGRAM OBJECT_OWNER OBJECT_NAME<br />

-------- --- -------------- ------- ------------ ---------------- --------------<br />

09:14:43 NO TRIVADIS\pej SCOTT sqlplusw.exe - Record(s)<br />

09:14:43 NO TRIVADIS\pej SCOTT sqlplusw.exe SCOTT EMP<br />

09:14:43 NO TRIVADIS\pej SCOTT sqlplusw.exe - Record(s)<br />

09:14:43 NO TRIVADIS\pej SCOTT sqlplusw.exe SCOTT EMP<br />

09:14:43 YES TRIVADIS\pej SCOTT sqlplusw.exe - Record(s)<br />

Sur la page "Tools & Services" de notre site web, vous pouvez télécharger gratuitement <strong>un</strong><br />

fichier contenant la table Create, la procédure Create et le trigger de base de données<br />

requis (cslcktrg.sql) ainsi qu'<strong>un</strong> fichier destiné à la version formatée (lslcksta.sql).


Pour obtenir davantage d'informations sur le comportement transactionnel, les blocages et<br />

les interblocages, n'hésitez pas à consulter d'anciennes éditions du DOAG et à visiter notre<br />

site web.<br />

Bonne chance avec Oracle! En espérant que vous saurez éviter les plantages et les attentes<br />

provoqués par les <strong>Deadlock</strong>s...<br />

Peter Jensch<br />

<strong>Trivadis</strong> AG E-mail: peter.jensch@trivadis.com<br />

Peter Jensch Tél.: +49 711 90 36 32 30<br />

Max-Lang-Strasse 56 Fax: +49 711 90 36 32 59<br />

D-70771 Leinfelden-Echterdingen Internet: http://www.trivadis.com

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!