ORA-00054: ressource occupée et acquisition avec NOWAIT spécifié ou expiration du délai


199

Pourquoi est-ce que j'obtiens cette erreur de base de données lorsque je mets à jour une table?

ERREUR à la ligne 1: ORA-00054: ressource occupée et acquisition avec NOWAIT spécifié ou timeout expiré


22
Cela aide généralement si vous publiez la déclaration qui conduit à l'erreur
Gary Myers

Réponses:


227

Votre table est déjà verrouillée par une requête. Par exemple, vous pouvez avoir exécuté "select for update" et n'avez pas encore validé / annulé et déclenché une autre requête de sélection. Faites un commit / rollback avant d'exécuter votre requête.


48
J'ajouterais «dans une autre session» à cela. Un scénario courant est que vous avez testé la mise à jour dans un outil, par exemple SQL Developer ou Toad, et que vous avez ensuite essayé de l'exécuter ailleurs pendant que la première session détient toujours le verrou. Vous devez donc valider / annuler l'autre session avant de pouvoir exécuter à nouveau la mise à jour.
Alex Poole

1
Très probablement DML (insérer / supprimer / mettre à jour) plutôt qu'une requête. Et dans une autre session. Juste parce que le gars qui a demandé semble être un débutant, la réponse peut être correcte. Mais vous NE POUVEZ PAS vous engager au nom d'autres utilisateurs dans un système de production.
Arturo Hernandez

4
Eh bien, ce qui m'a fait avoir ce problème était dans Toad: un collègue était dans la même table que moi lorsque je voulais supprimer une ligne, et donc je ne pouvais pas la supprimer. Quand il est passé à une autre table, j'ai pu supprimer des lignes. Cela peut peut-être aider quelqu'un là-bas. Mais ce n'est que si vous travaillez avec Toad dans les tables, et non pour les requêtes.
DatRid

s'est récemment produit sur notre serveur de test (application Spring sur WebSphere). La solution consistait à séparer le script de mise à jour de la base de données «monolithique» en plus petits morceaux en déplaçant les instructions à l'origine des erreurs dans un service de mise à jour distinct qui utilise une transaction distincte: @Transactional (propagation = Propagation.REQUIRES_NEW)
actc

104

à partir d'ici ORA-00054: ressource occupée et acquisition avec NOWAIT spécifié

Vous pouvez également rechercher le sql, le nom d'utilisateur, la machine, les informations de port et accéder au processus réel qui détient la connexion

SELECT O.OBJECT_NAME, S.SID, S.SERIAL#, P.SPID, S.PROGRAM,S.USERNAME,
S.MACHINE,S.PORT , S.LOGON_TIME,SQ.SQL_FULLTEXT 
FROM V$LOCKED_OBJECT L, DBA_OBJECTS O, V$SESSION S, 
V$PROCESS P, V$SQL SQ 
WHERE L.OBJECT_ID = O.OBJECT_ID 
AND L.SESSION_ID = S.SID AND S.PADDR = P.ADDR 
AND S.SQL_ADDRESS = SQ.ADDRESS;

5
J'ai dû supprimer le port de la requête mais cela m'a permis de trouver le coupable
Sibster

les verrous sont transitoires. donc si l'insert arrive, vous vous engagez immédiatement. il n'apparaîtra pas dans cette requête. vous pouvez toujours obtenir l'erreur si vous émettez le 'DDL'. pendant que la transaction est ouverte. voir mon message ci-dessous. C'est vraiment facile à contourner.
Bob

4
J'ai le même problème que l'OP, mais je ne vois aucune des tables que vous mentionnez (sélectionnez * à partir de V $ LOCKED_OBJECT, par exemple, retourne ORA-00942: la table ou la vue n'existe pas). Des idées?
random_forest_fanatic

3
Vous ne disposez peut-être pas des privilèges suffisants pour consulter les vues de gestion.
njplumridge

Suivi du S.Portproblème: la documentation 11.2 mentionne Portcomme champ dans V$Session, mais pour moi, l'utilisation de 11.1 S.Portest invalide. A-t-il été ajouté pour 11.2, peut-être?

67

Veuillez tuer la session Oracle

Utilisez la requête ci-dessous pour vérifier les informations de session active

SELECT
    O.OBJECT_NAME,
    S.SID,
    S.SERIAL#,
    P.SPID,
    S.PROGRAM,
    SQ.SQL_FULLTEXT,
    S.LOGON_TIME
FROM
    V$LOCKED_OBJECT L,
    DBA_OBJECTS O,
    V$SESSION S,
    V$PROCESS P,
    V$SQL SQ
WHERE
    L.OBJECT_ID = O.OBJECT_ID
    AND L.SESSION_ID = S.SID
    AND S.PADDR = P.ADDR
    AND S.SQL_ADDRESS = SQ.ADDRESS;

tuer comme

alter system kill session 'SID,SERIAL#';

(Par exemple alter system kill session '13,36543',;)

Référence http://abeytom.blogspot.com/2012/08/finding-and-fixing-ora-00054-resource.html


5
Devrait mettre en garde cette réponse avec quels privilèges sont requis. J'ai CONNECTet RESOURCE, mais pas ce dont il a besoin, avec le compte que j'ai, et dit ORA-00942: table or view does not exist. Tout le monde ne lisant pas ce fil n'aura pas le SYScompte.
vapcguy

Si vous ajoutez également la colonne de sélection «S.OSUSER», vous saurez quel utilisateur exécutait cette transaction et ce sera pratique au cas où vous voudriez vérifier avec l'utilisateur avant de tuer la session.
Narasimha

Si la session est suspendue, alter system kill session '13,36543'le délai expirera et la session ne mourra pas. Dans ce cas, voir: stackoverflow.com/a/24306610/587365
Andrew Spencer

Lors de l'insertion de données dans une table à l'aide d'un connection objectin, pythonj'ai eu une erreur. Ensuite, le python scriptest fermé sans fermer correctement le connection object. Maintenant, j'obtiens l'erreur "LOCKWAIT" lorsque j'essaye de déposer la table dans une autre session. Quand j'essaye, kill sessionje n'ai pas assez de privilèges. Que peut-on faire d'autre pour s'en débarrasser?
sjd

17

Il existe une solution très simple pour résoudre ce problème.

Si vous exécutez une trace 10046 sur votre session (google ceci ... trop d'explications). Vous verrez qu'avant toute opération DDL, Oracle effectue les opérations suivantes:

VERROUILLER LA TABLE "TABLE_NAME" PAS D'ATTENTE

Donc, si une autre session a une transaction ouverte, vous obtenez une erreur. Donc le correctif est ... roulement de tambour s'il vous plaît. Émettez votre propre verrou avant le DDL et laissez de côté le «PAS D'ATTENTE».

Note spéciale:

si vous divisez / supprimez des partitions, oracle verrouille simplement la partition. - pour que vous puissiez simplement verrouiller la sous-partition de partition.

Alors ... Les étapes suivantes résolvent le problème.

  1. VERROUILLER LA TABLE "NOM DE LA TABLE"; - vous allez «attendre» (les développeurs appellent cela suspendu). jusqu'à ce que la session avec la transaction ouverte, s'engage. Ceci est une file d'attente. il peut donc y avoir plusieurs sessions devant vous. mais vous ne ferez pas d'erreur.
  2. Exécutez DDL. Votre DDL exécutera alors un verrou avec le NO WAIT. Cependant, votre session a acquis le verrou. Alors tu es bon.
  3. Validation automatique DDL. Cela libère les serrures.

Les instructions DML «attendront» ou comme les développeurs l'appellent «se bloquer» pendant que la table est verrouillée.

J'utilise ceci dans le code qui fonctionne à partir d'un travail pour supprimer des partitions. Ça fonctionne bien. C'est dans une base de données qui s'insère constamment à un rythme de plusieurs centaines d'insertions / seconde. Aucune erreur.

si vous vous demandez. Faire cela en 11g. J'ai déjà fait cela en 10g dans le passé.


3
ok, c'est faux. en 11g, utilisez le set_ddl_timeout, ceci n'est disponible qu'en 11g. oracle effectue un commit avant de faire DDL, donc il libère le verrou. En 11g, vous pouvez faire attendre votre DDL. Je fais ça maintenant. Fonctionne très bien.
Bob

3
en 11g, vous devez utiliser LOCK TABLE nom_table EN MODE EXCLUSIF;
Kamil Roman

1
C'est vraiment utile si vous obtenez l'ORA-00054 à partir de travaux par lots, mais je soupçonne que la plupart des gens qui débarquent ici (y compris l'OP et moi) font du développement et ont laissé une session ouverte en faisant des insertions sur la même table qu'eux. essayez de laisser tomber et de recréer. KILL SESSIONest la bonne réponse pour ces personnes.
Andrew Spencer

@Bob Un exemple de code pour 11g et l'ancien serait bien. À quoi sert la syntaxe set_ddl_timeoutet que devrait-elle être?
vapcguy

1
@vapcguy cela a fonctionné pour moi sur 11g: ALTER SYSTEM SET ddl_lock_timeout=20;voir la documentation
rshdev

13

Cette erreur se produit lorsque la ressource est occupée. Vérifiez si vous avez des contraintes référentielles dans la requête. Ou même les tables que vous avez mentionnées dans la requête peuvent être occupées. Ils peuvent être engagés dans un autre travail qui sera définitivement répertorié dans les résultats de requête suivants:

SELECT * FROM V$SESSION WHERE STATUS = 'ACTIVE'

Trouvez le SID,

SELECT * FROM V$OPEN_CURSOR WHERE SID = --the id

1
Tout le monde n'aura pas accès à ces vues. Je reçois ORA-00942: table or view does not exist.
vapcguy

Dans de tels cas, les administrateurs DB sont responsables de fournir ces informations.
Arunchunaivendan

Pas toujours. J'ai dû essayer de faire comprendre au code cela et de le contourner, et ni moi ni mon compte de service n'aurons ces droits.
vapcguy

8

Cela se produit lorsqu'une session autre que celle utilisée pour modifier une table détient un verrou probablement à cause d'un DML (mise à jour / suppression / insertion). Si vous développez un nouveau système, il est probable que vous ou quelqu'un de votre équipe émettiez la déclaration de mise à jour et vous pourriez tuer la session sans trop de conséquences. Ou vous pouvez vous engager à partir de cette session une fois que vous savez qui a ouvert la session.

Si vous avez accès à un système d'administration SQL, utilisez-le pour rechercher la session incriminée. Et peut-être le tuer.

Vous pouvez utiliser v $ session et v $ lock et autres, mais je vous suggère sur Google comment trouver cette session et comment la tuer.

Dans un système de production, cela dépend vraiment. Pour oracle 10g et plus ancien, vous pouvez exécuter

LOCK TABLE mytable in exclusive mode;
alter table mytable modify mycolumn varchar2(5);

Dans une session séparée, mais préparez les éléments suivants au cas où cela prendrait trop de temps.

alter system kill session '....

Cela dépend du système dont vous disposez, les anciens systèmes sont plus susceptibles de ne pas s'engager à chaque fois. C'est un problème car il peut y avoir des serrures de longue date. Ainsi, votre verrou empêcherait tout nouveau verrou et attendrait un verrou qui, qui sait quand, sera libéré. C'est pourquoi vous avez prêt l'autre déclaration. Ou vous pouvez rechercher des scripts PLSQL qui font automatiquement des choses similaires.

Dans la version 11g, il existe une nouvelle variable d'environnement qui définit un temps d'attente. Je pense que cela fait probablement quelque chose de similaire à ce que j'ai décrit. N'oubliez pas que les problèmes de verrouillage ne disparaissent pas.

ALTER SYSTEM SET ddl_lock_timeout=20;
alter table mytable modify mycolumn varchar2(5);

Enfin, il peut être préférable d'attendre qu'il y ait peu d'utilisateurs dans le système pour effectuer ce type de maintenance.


Et comment trouver la session à tuer alter system kill session '....si vous n'avez pas accès aux vues de gestion?
vapcguy

Ça je ne sais pas.
Arturo Hernandez

7

Dans mon cas, j'étais tout à fait sûr que c'était l'une de mes propres sessions qui bloquait. Par conséquent, il était sûr de faire ce qui suit:

  • J'ai trouvé la session offensante avec:

    SELECT * FROM V$SESSION WHERE OSUSER='my_local_username';

    La session était inactive , mais elle tenait toujours le verrou d'une manière ou d'une autre. Notez que vous devrez peut-être utiliser une autre condition WHERE dans votre cas (par exemple essayer USERNAMEou des MACHINEchamps).

  • Vous avez tué la session en utilisant IDet SERIAL#acquis ci-dessus:

    alter system kill session '<id>, <serial#>';

Edité par @thermz: si aucune des requêtes précédentes en session ouverte ne fonctionne, essayez celle-ci. Cette requête peut vous aider à éviter les erreurs de syntaxe lors de la suppression de sessions:

  • SELECT 'ALTER SYSTEM KILL SESSION '''||SID||','||SERIAL#||''' immediate;' FROM V$SESSION WHERE OSUSER='my_local_username_on_OS'

Si aucune des requêtes de session ouverte précédentes ne fonctionne, essayez celle-ci.
thermz

5

Vérifiez simplement le processus qui tient la session et tuez-le. Son retour à la normale.

Ci-dessous SQL trouverez votre processus

SELECT s.inst_id,
   s.sid,
   s.serial#,
   p.spid,
   s.username,
   s.program FROM   gv$session s
   JOIN gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id;

Alors tuez-le

ALTER SYSTEM KILL SESSION 'sid,serial#'

OU

un exemple que j'ai trouvé en ligne semble avoir besoin de l'ID d'instance ainsi que de modifier la session d'arrêt du système '130,620, @ 1';


5

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;
   
   ALTER SYSTEM KILL SESSION 'sid,serial#';



3

J'ai réussi à frapper cette erreur en créant simplement une table! Il n'y avait évidemment pas de problème de contention sur une table qui n'existait pas encore. L' CREATE TABLEinstruction contenait une CONSTRAINT fk_name FOREIGN KEYclause faisant référence à une table bien remplie. J'ai dû:

  • Supprimez la clause FOREIGN KEY de l'instruction CREATE TABLE
  • Créer un INDEX sur la colonne FK
  • Créer le FK

J'ai juste eu le même problème. J'ai pu créer la table après avoir supprimé la définition fk de l'instruction ddl, mais je ne peux pas l'ajouter même après avoir créé l'index sur la colonne.
vadipp

J'ai pu le résoudre. Tout d'abord, j'ai ajouté une novalidateclause au alter table add constraint. Cela permet en quelque sorte de fonctionner, mais il se verrouille. Ensuite, j'ai regardé les verrous de session pour savoir sur quelle session il verrouille. Et puis j'ai tué cette session.
vadipp

3

J'ai eu cette erreur quand j'avais 2 scripts que j'exécutais. J'avais:

  • Une session SQL * Plus connectée directement à l'aide d'un compte utilisateur de schéma (compte n ° 1)
  • Une autre session SQL * Plus connectée à l'aide d'un compte d'utilisateur de schéma différent (compte n ° 2), mais se connectant via un lien de base de données en tant que premier compte

J'ai exécuté une table drop, puis la création de table en tant que compte n ° 1. J'ai exécuté une mise à jour de table sur la session du compte n ° 2. N'a pas validé les modifications. Script de suppression / création de table réexécuté en tant que compte n ° 1. Vous avez une erreur sur la drop table xcommande.

Je l'ai résolu en exécutant COMMIT;la session SQL * Plus du compte n ° 2.


Si vous ne disposez pas des autorisations nécessaires pour supprimer une table, que ferez-vous? Mauvaise réponse
Shakeer Hussain

1
Shakeer, c'était juste un exemple de ce que je faisais quand cela m'est arrivé - pas la solution au problème - c'était dans ma toute dernière ligne - exécuter un COMMIT;. Si vous ne pouvez même pas supprimer une table, vous ne disposez pas des autorisations nécessaires pour modifier tout ce qui pourrait vous amener à résoudre ce problème en premier lieu.
vapcguy

-2

Je suis également confronté au problème similaire. Rien à faire du programmeur pour résoudre cette erreur. J'ai informé mon équipe oracle DBA. Ils tuent la session et ont travaillé comme un charme.


1
Quelqu'un doit encore faire quelque chose. Que faire si l'équipe DBA ne sait pas quoi faire et comment tuer une session? Mauvaise réponse.
vapcguy

1
Si DBA ne sait pas comment tuer la session, il est inapte au travail
Shakeer Hussain

Tout le monde a besoin d'un rappel de syntaxe de temps en temps pour éviter de se tromper.
vapcguy

-5

La solution donnée par le lien de Shashi est la meilleure ... pas besoin de contacter dba ou quelqu'un d'autre

faire une sauvegarde

create table xxxx_backup as select * from xxxx;

supprimer toutes les lignes

delete from xxxx;
commit;

insérez votre sauvegarde.

insert into xxxx (select * from xxxx_backup);
commit;

11
supprimer / tronquer ne sont pas interchangeables. l'exécution d'un grand nombre de suppressions a d'énormes implications sur les performances. C'est vraiment mauvais.
Bob

Je pensais que c'était un modèle courant.
Shawn Xue
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.