Ошибка ORA-00054: resource busy and acquire with NOWAIT в Oracle DB
- Ошибка
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expiredпри выполнении DDL-команд (ALTER TABLE,CREATE INDEX,DROP TABLE) илиSELECT FOR UPDATE NOWAIT. - Невозможность наложить монопольную блокировку (Exclusive DDL Lock) из-за незафиксированных транзакций других пользователей.
- Сбои скриптов миграции схем баз данных и обновлений конфигураций 1С.
1. Поиск блокирующих объектов и сессий
SELECT s.sid, s.serial#, s.username, s.osuser, s.machine, s.program,
l.locked_mode, o.owner, o.object_name, o.object_type
FROM v$locked_object l
JOIN dba_objects o ON l.object_id = o.object_id
JOIN v$session s ON l.session_id = s.sid
WHERE o.object_name = 'ИМЯ_ТАБЛИЦЫ';2. Использование параметра DDL_LOCK_TIMEOUT
Вместо мгновенного сбоя настройте время ожидания освобождения объекта в рамках текущей сессии перед выполнением DDL:
-- Установка ожидания снятия блокировок до 60 секунд
ALTER SESSION SET ddl_lock_timeout = 60;
-- Выполнение DDL операции
ALTER TABLE my_table ADD (new_column VARCHAR2(100));3. Построение индексов без блокировки чтения/записи (ONLINE)
CREATE INDEX idx_my_table_col ON my_table(col) ONLINE;4. Завершение блокирующей сессии (при необходимости)
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE; Частые вопросы (FAQ)
Почему ORA-00054 возникает при обычном ALTER TABLE, если в таблице нет активных изменений?
Для DDL операции требуется исключительная блокировка (Exclusive Table Lock mode 6). Если хотя бы одна транзакция удерживает даже разделяемую блокировку (Row Share / Row Exclusive) через незакоммиченный SELECT FOR UPDATE или единичный INSERT, DDL операция немедленно завершится с ORA-00054.
Помогает ли опция ONLINE избежать ORA-00054?
Опция ONLINE значительно снижает вероятность ошибки, так как требует кратковременной эксклюзивной блокировки только в начале и в конце операции, позволяя пользователям продолжать DML во время перестроения.
Какое значение по умолчанию имеет параметр ddl_lock_timeout?
По умолчанию ddl_lock_timeout = 0, что означает мгновенное завершение DDL с ошибкой ORA-00054 при наличии любой конфликтующей блокировки (NOWAIT режим).
Как найти SQL-запрос, который держит блокировку на объекте?
Свяжите v$session и v$sql по полям sql_id или prev_sql_id: SELECT sql_text FROM v$sql WHERE sql_id = (SELECT prev_sql_id FROM v$session WHERE sid = <SID_блокировщика>).