Introduction Comment survient un Deadlock? Comment ... - Trivadis
Introduction Comment survient un Deadlock? Comment ... - Trivadis
Introduction Comment survient un Deadlock? Comment ... - Trivadis
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