ORA-00054: ресурс занят и получен с указанным значением NOWAIT или истекло время ожидания


193

Почему я получаю эту ошибку базы данных при обновлении таблицы?

ОШИБКА в строке 1: ORA-00054: ресурс занят и получен с указанным значением NOWAIT или истекло время ожидания


22
Как правило, это помогает, если вы публикуете утверждение, которое приводит к ошибке
Гари Майерс

Ответы:


223

Ваша таблица уже заблокирована каким-либо запросом. Например, вы, возможно, выполнили команду «выбрать для обновления» и еще не зафиксировали / откатили и не запустили еще один запрос на выборку. Сделайте коммит / откат перед выполнением вашего запроса.


47
Я бы добавил «в другой сессии» к этому. Одним из распространенных сценариев является то, что вы протестировали обновление в инструменте, скажем, SQL Developer или Toad, а затем попытались запустить его где-нибудь еще, пока первый сеанс все еще удерживает блокировку. Поэтому вам нужно зафиксировать / откатить другой сеанс, прежде чем вы сможете снова запустить обновление.
Алекс Пул

1
Скорее всего DML (вставка / удаление / обновление), а не запрос. И в другой сессии. Просто потому, что парень, который спросил, кажется новичком, ответ может быть правильным. Но вы НЕ МОЖЕТЕ совершать обязательства от имени других пользователей в производственной системе.
Артуро Эрнандес

4
Ну, что заставило меня столкнуться с этой проблемой, так это в Toad: коллега был в той же таблице, что и я, когда я хотел удалить строку, и поэтому я не мог удалить ее. Когда он переключился на другую таблицу, я смог удалить строки. Это может помочь кому-то там. Но это только если вы работаете с Toad внутри таблиц, а не для запросов.
DatRid

недавно произошло на нашем промежуточном сервере (приложение Spring в WebSphere). Решением было разделить «монолитный» сценарий обновления базы данных на более мелкие части, переместив операторы, вызывающие ошибки, в отдельную службу обновления, которая использует отдельную транзакцию: @Transactional (распространение = Propagation.REQUIRES_NEW)
actc

103

отсюда ORA-00054: ресурс занят и получен с указанным NOWAIT

Вы также можете посмотреть информацию о sql, имени пользователя, машине, порте и перейти к фактическому процессу, который содержит соединение

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
Мне пришлось удалить s.port из запроса, но это позволило мне найти виновника
Sibster

замки временные. так что, если вставка приходит, то вы немедленно фиксируете. это не будет отображаться в этом запросе. вы все равно можете получить ошибку, если вы введете «DDL». пока транзакция открыта. см. мой пост ниже. Это действительно легко обойти.
Боб

4
У меня та же проблема, что и у OP, но я не вижу ни одной из упомянутых вами таблиц (например, select * from V $ LOCKED_OBJECT возвращает ORA-00942: таблица или представление не существует). Любые идеи?
random_forest_fanatic

3
Возможно, у вас недостаточно прав для просмотра представлений управления.
njplumridge

Продолжение S.Portпроблемы: документы 11.2 упоминаются Portкак поле в V$Session, но для меня, использование 11.1, S.Portнедействительно. Может быть, это было добавлено для 11.2?

64

Пожалуйста, убейте сессию Oracle

Используйте запрос ниже, чтобы проверить информацию об активной сессии

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;

убить как

alter system kill session 'SID,SERIAL#';

(Например alter system kill session '13,36543',;)

Ссылка http://abeytom.blogspot.com/2012/08/finding-and-fixing-ora-00054-resource.html


5
Следует предостеречь этот ответ от того, какие привилегии требуются. У меня есть CONNECTи RESOURCE, но не все, что это нужно, с учетной записью, которую я имею, и говорит ORA-00942: table or view does not exist. Не каждый, кто читает эту ветку, будет иметь SYSучетную запись.
vapcguy

Если вы также добавите столбец выбора «S.OSUSER», вы будете знать, какой пользователь выполнял эту транзакцию, и это будет удобно, если вы захотите проверить пользователя перед тем, как убить сеанс.
Нарасимха

Если сеанс завис, то alter system kill session '13,36543'истечет время ожидания и сеанс не умрет. В этом случае см .: stackoverflow.com/a/24306610/587365
Эндрю Спенсер

При вставке данных в таблицу с использованием connection objectв pythonя получил некоторую ошибку. Затем python scriptзакрывается без надлежащего закрытия connection object. Теперь я получаю ошибку "LOCKWAIT", когда я пытаюсь удалить таблицу в другой сессии. Когда я пытаюсь, у kill sessionменя недостаточно привилегий. Что еще можно сделать, чтобы избавиться от этого?
19

17

Существует очень легко обойти эту проблему.

Если вы выполняете трассировку 10046 в своей сессии (Google это ... слишком много, чтобы объяснить). Вы увидите, что перед любой операцией DDL Oracle делает следующее:

LOCK TABLE 'TABLE_NAME' NO WAIT

Так что, если в другой сессии есть открытая транзакция, вы получите ошибку. Так что исправить это ... барабанная дробь, пожалуйста. Создайте свой собственный замок перед DDL и пропустите «NO WAIT».

Специальная записка:

если вы делаете разбиение / удаление разделов, oracle просто блокирует раздел. - так что вы можете просто заблокировать подраздел раздела.

Итак ... Следующие шаги решают проблему.

  1. LOCK TABLE 'TABLE NAME'; - вы будете «ждать» (разработчики называют это зависанием). до сеанса с открытой транзакцией, фиксирует. Это очередь. так что может быть несколько сессий впереди вас. но вы не ошибетесь.
  2. Выполнить DDL. Затем ваш DDL запустит блокировку без ожидания. Тем не менее, ваша сессия приобрела замок. Так что ты хороший.
  3. DDL авто-коммит. Это освобождает замки.

Операторы DML будут «ждать», или, как разработчики называют, «зависать», пока таблица заблокирована.

Я использую это в коде, который запускается из задания для удаления разделов. Работает нормально. Он находится в базе данных, которая постоянно вставляется со скоростью несколько сотен вставок в секунду. Нет ошибок

если вам интересно Делать это в 11г. Я делал это в 10g и раньше, и раньше.


3
хорошо это неправильно в 11g используйте set_ddl_timeout, это доступно только в 11g. oracle делает коммит перед выполнением DDL, поэтому снимает блокировку. В 11g вы можете ждать DDL. Я делаю это сейчас. Работает отлично.
Боб

3
в 11g вы должны использовать LOCK TABLE table_name В ЭКСКЛЮЗИВНОМ РЕЖИМЕ;
Камиль Роман

1
Это действительно полезно, если вы получаете ORA-00054 от пакетных заданий, но я подозреваю, что большинство людей, приземляющихся здесь (включая OP и меня), занимаются какой-то разработкой и оставили сеанс открытым, делая вставки в той же таблице, что и они. пытаемся уронить и воссоздать. KILL SESSIONэто правильный ответ для этих людей.
Эндрю Спенсер

@Bob Пример кода для 11g и старого способа был бы неплох. Для чего нужен синтаксис set_ddl_timeoutи каким он должен быть?
vapcguy

1
@vapcguy это сработало для меня на 11g: ALTER SYSTEM SET ddl_lock_timeout=20;см. документы
rshdev

13

Эта ошибка происходит, когда ресурс занят. Проверьте, есть ли у вас ссылочные ограничения в запросе. Или даже таблицы, которые вы упомянули в запросе, могут быть заняты. Они могут быть заняты другой работой, которая обязательно будет указана в следующих результатах запроса:

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

Найти SID,

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

1
Не каждый будет иметь доступ к этим взглядам. Я получаю ORA-00942: table or view does not exist.
vapcguy

В таких случаях администраторы БД несут ответственность за предоставление этой информации.
Арунчунайвендан

Не всегда. Я должен был попытаться заставить код понять это и обойти это, и ни я, ни моя служебная учетная запись не будут иметь этих прав.
vapcguy

7

Это происходит, когда сеанс, отличный от сеанса, используемого для изменения таблицы, удерживает блокировку, вероятно, из-за DML (обновление / удаление / вставка). Если вы разрабатываете новую систему, вполне вероятно, что вы или кто-то из вашей команды выдает оператор обновления, и вы можете прервать сеанс без особых последствий. Или вы можете выполнить коммит из этого сеанса, как только узнаете, у кого этот сеанс открыт.

Если у вас есть доступ к системе администрирования SQL, используйте ее, чтобы найти нарушающий сеанс. И, возможно, убить его.

Вы можете использовать v $ session и v $ lock и другие, но я предлагаю вам Google, как найти этот сеанс и как его убить.

В производственной системе это действительно зависит. Для оракула 10g и старше вы можете выполнить

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

В отдельном сеансе, но подготовьте следующее, если это займет слишком много времени.

alter system kill session '....

Это зависит от того, какая у вас система, более старые системы, скорее всего, не будут фиксировать каждый раз. Это проблема, так как могут быть давно стоящие замки. Таким образом, ваша блокировка предотвратит любые новые блокировки и будет ждать блокировки, которая знает, когда она будет снята. Вот почему у вас есть другое заявление готово. Или вы можете поискать сценарии PLSQL, которые делают подобные вещи автоматически.

В версии 11g появилась новая переменная среды, которая устанавливает время ожидания. Я думаю, что это, вероятно, делает что-то похожее на то, что я описал. Имейте в виду, что проблемы с блокировкой не исчезают.

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

Наконец, может быть лучше подождать, пока в системе не останется мало пользователей, которые могли бы выполнять этот вид обслуживания.


И как вы узнаете сеанс, чтобы убить за alter system kill session '.... если у вас нет доступа к представлениям управления?
vapcguy

Это я не знаю.
Артуро Эрнандес

7

В моем случае я был совершенно уверен, что это был один из моих собственных сеансов, который блокировал. Поэтому было безопасно сделать следующее:

  • Я нашел оскорбительный сеанс с:

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

    Сеанс был неактивным , но все равно как-то удерживал блокировку. Обратите внимание, что вам может понадобиться использовать другое условие WHERE в вашем случае (например, try USERNAMEили MACHINEfields).

  • Убил сеанс с помощью IDи SERIAL#приобрел выше:

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

Под редакцией @thermz: Если ни один из предыдущих запросов open-session не работает, попробуйте этот. Этот запрос может помочь вам избежать синтаксических ошибок при уничтожении сессий:

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

Если ни один из предыдущих запросов open-session не работает, попробуйте этот.
терм

5

Просто проверьте процесс проведения сеанса и убейте его. Спина к норме.

Ниже SQL найдет ваш процесс

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;

Тогда убей это

ALTER SYSTEM KILL SESSION 'sid,serial#'

ИЛИ

некоторые примеры, которые я нашел в сети, похоже, нуждаются в идентификаторе экземпляра, а также в сеансе уничтожения системы «130 620, @ 1»;



4

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

Мне удалось поразить эту ошибку при простом создании таблицы! Очевидно, на столе, который еще не существовал, не было проблем с конкуренцией. В CREATE TABLEзаявлении содержался CONSTRAINT fk_name FOREIGN KEYпункт, ссылающийся на хорошо заполненную таблицу. Мне пришлось:

  • Удалите предложение FOREIGN KEY из оператора CREATE TABLE
  • Создайте ИНДЕКС для столбца FK
  • Создать ФК

У меня просто была такая же проблема. Мне удалось создать таблицу после удаления определения fk из оператора ddl, но я не могу добавить ее даже после создания индекса для столбца.
vadipp

Я смог решить это. Во-первых, я добавил novalidateпункт к alter table add constraint. Это как-то позволяет ему работать, но оно блокируется. Затем я посмотрел на блокировки сеанса, чтобы выяснить, на каком сеансе он блокируется. А потом я убил эту сессию.
vadipp

3

Эта ошибка произошла, когда у меня было 2 скрипта, которые я запускал. Я имел:

  • Сеанс SQL * Plus, подключенный напрямую с использованием учетной записи пользователя схемы (учетная запись № 1)
  • Другой сеанс SQL * Plus, подключенный с использованием другой учетной записи пользователя схемы (учетная запись № 2), но подключение через ссылку на базу данных в качестве первой учетной записи

Я запустил удаление таблицы, затем создание таблицы под учетной записью № 1. Я запустил обновление таблицы во время сеанса № 2. Не совершал изменений. Повторно запустил скрипт удаления / создания таблицы в качестве учетной записи # 1. Ошибка в drop table xкоманде.

Я решил это, запустив COMMIT;сеанс SQL * Plus учетной записи № 2.


Если у вас нет прав на удаление таблицы, что вы будете делать? Плохой ответ
Шейкер Хуссейн

1
Shakeer, это был просто пример того, что я делал, когда это случилось со мной, а не решение проблемы - это было в моей последней строке - запуск COMMIT;. Если вы даже не можете удалить таблицу, у вас нет прав на изменение чего-либо, что могло бы привести вас к этой проблеме.
vapcguy

-2

Я также сталкиваюсь с подобной проблемой. Программисту ничего не нужно делать, чтобы устранить эту ошибку. Я сообщил своей команде оракула DBA. Они убивают сеанс и работают как заклинание.


1
Кто-то еще должен что-то сделать. Что если команда DBA не знает, что делать и как убить сеанс? Плохой ответ
vapcguy

1
Если администратор
базы

Всем нужно время от времени обновлять синтаксис, чтобы не ошибиться.
vapcguy

-5

Решение, данное ссылкой Шаши, является лучшим ... не нужно связываться с dba или кем-то еще

сделать резервную копию

create table xxxx_backup as select * from xxxx;

удалить все строки

delete from xxxx;
commit;

вставьте свою резервную копию.

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

10
удалить / усечь не являются взаимозаменяемыми. выполнение большого количества удалений имеет огромное значение для производительности. Это действительно плохо.
Боб

Я думал, что это общая картина.
Шон Сюэ,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.