Nous avons une base de données Oracle et la table des comptes clients contient environ un million de lignes. Au fil des ans, nous avons construit quatre interfaces utilisateur différentes (deux dans Oracle Forms, deux dans .Net), qui restent toutes utilisées. Nous avons également un certain nombre de tâches en arrière-plan (à la fois persistantes et planifiées).
Quelque chose tient parfois un long verrou (plus de 30 secondes, par exemple) sur une ligne du tableau des comptes, ce qui entraîne l'échec de l'une des tâches persistantes en arrière-plan. La tâche en arrière-plan en question redémarre d'elle-même une fois la mise à jour terminée. Nous le découvrons quelques minutes après que cela se soit produit, mais le verrou a été relâché.
Nous avons des raisons de croire que cela pourrait être une mauvaise assurance-chômage, mais nous n'avons pas été en mesure de trouver un "pistolet fumant".
J'ai trouvé des requêtes qui répertorient des blocs, mais c'est pour quand vous avez deux travaux en lice pour une ligne. Je veux savoir quelles lignes ont un verrou lorsqu'il n'y a pas nécessairement un deuxième travail pour essayer d'obtenir un verrou.
Nous sommes sur 11g, mais nous connaissons le problème depuis 8i.
Le concept de verrouillage de Oracle
est assez différent de celui des autres systèmes.
Lorsqu'une ligne dans Oracle
est verrouillée, l'enregistrement lui-même est mis à jour avec la nouvelle valeur (le cas échéant) et, en outre, un verrou (qui est essentiellement un pointeur sur le verrou de transaction qui réside dans le segment d'annulation) est placé directement dans l'enregistrement. .
Cela signifie que verrouiller un enregistrement dans Oracle
signifie mettre à jour les métadonnées de l'enregistrement et émettre une page logique en écriture. Par exemple, vous ne pouvez pas faire SELECT FOR UPDATE
sur un tablespace en lecture seule.
De plus, les enregistrements eux-mêmes ne sont pas mis à jour après la validation: le segment d'annulation est mis à jour.
Cela signifie que chaque enregistrement contient des informations sur la dernière transaction mise à jour, même si la transaction elle-même est morte depuis longtemps. Pour savoir si la transaction est active ou non (et donc si l'enregistrement est actif ou non), il est nécessaire de visiter le segment d'annulation.
Oracle ne dispose pas d'un gestionnaire de verrous traditionnel, ce qui signifie que l'obtention d'une liste de tous les verrous nécessite l'analyse de tous les enregistrements de tous les objets. Cela prendrait trop de temps.
Vous pouvez obtenir des verrous spéciaux, tels que des objets de métadonnées verrouillés (en utilisant v$locked_object
), des verrous en attente (en utilisant v$session
), etc., mais pas la liste de tous les verrous de tous les objets de la base de données.
Regardez dba_blockers, dba_waiters et dba_locks pour le verrouillage. Les noms doivent être explicites.
Vous pouvez créer un travail qui s'exécute, par exemple, une fois par minute et enregistre les valeurs dans dba_blockers et le sql_id actif en cours pour cette session. (via v $ session et v $ sqlstats).
Vous pouvez également vouloir regarder dans v $ sql_monitor. Ce sera le journal par défaut de tous les SQL qui prend plus de 5 secondes. Il est également visible sur la page Surveillance SQL dans Enterprise Manager.
Plutôt que de verrouiller, je vous suggère de regarder les transactions de longue durée , en utilisant v$transaction
. À partir de là, vous pouvez rejoindre v$session
, ce qui devrait vous donner une idée de l'interface utilisateur (essayez les colonnes programme et machine) ainsi que de l'utilisateur.
vous pouvez trouver les tables verrouillées dans oracle en interrogeant avec la requête suivante
select
c.owner,
c.object_name,
c.object_type,
b.sid,
b.serial#,
b.status,
b.osuser,
b.machine
from
v$locked_object a ,
v$session b,
dba_objects c
where
b.sid = a.session_id
and
a.object_id = c.object_id;
Le code ci-dessous trouvera toutes les lignes verrouillées dans une table.
(Cependant, vous n’avez probablement pas besoin d’exécuter ce code. Si vous rencontrez un problème de verrouillage, il est généralement plus facile de trouver le coupable en utilisant GV$SESSION.BLOCKING_SESSION
et d’autres affichages du dictionnaire de données associés. Veuillez essayer une autre approche avant d’exécuter ce code extrêmement lent. )
Commençons par créer un exemple de table et des données. Exécutez ceci dans la session n ° 1.
--Sample schema.
create table test_locking(a number);
insert into test_locking values(1);
insert into test_locking values(2);
commit;
update test_locking set a = a+1 where a = 1;
Dans la session n ° 2, créez une table pour contenir les ROWID verrouillés.
--Create table to hold locked ROWIDs.
create table locked_rowids(the_rowid rowid);
--Remove old rows if table is already created:
--delete from locked_rowids;
--commit;
Dans la session 2, exécutez ce bloc PL/SQL pour lire la table entière, analyser chaque ligne et stocker les ROWID verrouillés. Soyez averti, cela peut être ridiculement lent. Dans votre version réelle de cette requête, remplacez les deux références par TEST_LOCKING par votre propre table.
--Save all locked ROWIDs from a table.
--WARNING: This PL/SQL block will be slow and will temporarily lock rows.
--You probably don't need this information - it's usually good enough to know
--what other sessions are locking a statement, which you can find in
--GV$SESSION.BLOCKING_SESSION.
declare
v_resource_busy exception;
pragma exception_init(v_resource_busy, -00054);
v_throwaway number;
type rowid_nt is table of rowid;
v_rowids rowid_nt := rowid_nt();
begin
--Loop through all the rows in the table.
for all_rows in
(
select rowid
from test_locking
) loop
--Try to look each row.
begin
select 1
into v_throwaway
from test_locking
where rowid = all_rows.rowid
for update nowait;
--If it doesn't lock, then record the ROWID.
exception when v_resource_busy then
v_rowids.extend;
v_rowids(v_rowids.count) := all_rows.rowid;
end;
rollback;
end loop;
--Display count:
dbms_output.put_line('Rows locked: '||v_rowids.count);
--Save all the ROWIDs.
--(Row-by-row because ROWID type is weird and doesn't work in types.)
for i in 1 .. v_rowids.count loop
insert into locked_rowids values(v_rowids(i));
end loop;
commit;
end;
/
Enfin, nous pouvons afficher les lignes verrouillées en rejoignant la table LOCKED_ROWIDS.
--Display locked rows.
select *
from test_locking
where rowid in (select the_rowid from locked_rowids);
A
-
1