Как найти заблокированные строки в Oracle

У нас есть база данных Oracle, а таблица учетных записей клиентов насчитывает около миллиона строк. На протяжении многих лет мы создали четыре разных пользовательских интерфейса (два в Oracle Forms, два в .Net), все из которых остаются в использовании. У нас есть ряд фоновых задач (как постоянных, так и запланированных).

Что-то иногда держит длинную блокировку (скажем, более 30 секунд) в строке в таблице учетных записей, что приводит к сбою одной из постоянных фоновых задач. Запрашиваемая фоновая задача перезапустится после истечения времени обновления. Мы узнаем об этом через несколько минут после этого, но к тому времени блокировка была выпущена.

У нас есть основания полагать, что это может быть неправильный пользовательский интерфейс, но он не смог найти "курящее оружие".

Я нашел несколько запросов, которые перечисляют блоки, но это, когда у вас есть два задания, конкурирующих за строку. Я хочу знать, в каких строках есть блокировки, когда не требуется вторая работа, пытающаяся получить блокировку.

Мы находимся на 11g, но испытываем проблему с 8i.

Ответ 1

Oracle Концепция блокировки существенно отличается от концепции других систем.

Когда строка в Oracle блокируется, сама запись обновляется с новым значением (если оно есть) и, кроме того, блокировка (которая по существу является указателем на блокировку транзакции, которая находится в сегменте отката) помещенный прямо в запись.

Это означает, что запись записи в Oracle означает обновление метаданных записи и выдачу логической записи страницы. Например, вы не можете сделать SELECT FOR UPDATE в табличном пространстве только для чтения.

Более того, сами записи не обновляются после фиксации: вместо этого сегмент отката обновляется.

Это означает, что каждая запись содержит некоторую информацию о транзакции, которая в последний раз ее обновляла, даже если сама транзакция уже давно умерла. Чтобы узнать, жива или нет транзакция (и, следовательно, если запись живая или нет), необходимо посетить сегмент отката.

В Oracle нет традиционного менеджера блокировок, а это значит, что для получения списка всех блокировок требуется сканирование всех записей во всех объектах. Это займет слишком много времени.

Вы можете получить некоторые специальные блокировки, такие как заблокированные объекты метаданных (с помощью v$locked_object), блокировка ждет (используя v$session) и т.д., но не список всех блокировок для всех объектов в базе данных.

Ответ 2

Посмотрите на dba_blockers, dba_waiters и dba_locks для блокировки. Имена должны быть самоочевидными.

Вы можете создать задание, которое будет запускаться, скажем, раз в минуту и регистрировать значения в dba_blockers и текущем активном sql_id для этого сеанса. (через v$session и v$sqlstats).

Вы также можете посмотреть в v$sql_monitor. Это будет журнал по умолчанию для всего SQL, который занимает более 5 секунд. Это также видно на странице "Мониторинг SQL" в Enterprise Manager.

Ответ 3

Вместо блокировок я предлагаю вам просмотреть долгосрочные транзакции, используя v$transaction. Оттуда вы можете присоединиться к v$session, что должно дать вам представление об пользовательском интерфейсе (попробуйте указать столбцы программы и машины), а также пользователя.

Ответ 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;

Ответ 5

Приведенный ниже блок PL/SQL находит все заблокированные строки в таблице. Остальные ответы только находят сеанс блокировки, для нахождения фактически заблокированных строк требуется чтение и проверка каждой строки.

(Тем не менее, вам, вероятно, не нужно запускать этот код. Если у вас возникла проблема с блокировкой, обычно легче найти виновного с помощью GV$SESSION.BLOCKING_SESSION и других связанных с ними представлений словаря данных. Пожалуйста, попробуйте другой подход, прежде чем запустить этот ужасно медленный код.)

Во-первых, давайте создадим образец таблицы и некоторые данные. Запустите это на занятии №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;

В сеансе № 2 создайте таблицу для хранения заблокированных идентификаторов ROWID.

--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;

В сеансе № 2 запустите этот блок PL/SQL, чтобы прочитать всю таблицу, проверить каждую строку и сохранить заблокированные ROWID. Имейте в виду, это может быть смехотворно медленным. В вашей реальной версии этого запроса измените обе ссылки на TEST_LOCKING на свою собственную таблицу.

--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 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 does not 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 does not work in types.)
    for i in 1 .. v_rowids.count loop
        insert into locked_rowids values(v_rowids(i));
    end loop;
    commit;
end;
/

Наконец, мы можем просмотреть заблокированные строки, присоединившись к таблице LOCKED_ROWIDS.

--Display locked rows.
select *
from test_locking
where rowid in (select the_rowid from locked_rowids);


A
-
1