web-dev-qa-db-ja.com

Oracleでロックされた行を見つける方法

Oracleデータベースがあり、顧客アカウントテーブルには約100万行があります。長年にわたり、4つの異なるUI(2つはOracle Forms、2つは.Net)を構築してきましたが、そのすべてが引き続き使用されています。多くのバックグラウンドタスク(永続的およびスケジュール済みの両方)もあります。

アカウントテーブルの行で何かが長いロック(たとえば、30秒を超える)を保持していることがあり、これにより永続的なバックグラウンドタスクの1つが失敗します。問題のバックグラウンドタスクは、更新がタイムアウトすると自動的に再起動します。それが起こってから数分後にそれを知りましたが、それまでにロックは解除されました。

UIが誤動作している可能性があると考える理由はありますが、「煙る銃」を見つけることができませんでした。

ブロックをリストするクエリをいくつか見つけましたが、それは、1つの行で競合する2つのジョブがある場合に当てはまります。ロックを取得しようとしている2番目のジョブが必ずしも存在しない場合に、どの行にロックがあるかを知りたい。

11gを使用していますが、8iから問題が発生しています。

16
ehymel

Oracleのロックの概念は、他のシステムのものとはかなり異なります。

Oracleの行がロックされると、レコード自体が新しい値(存在する場合)で更新され、さらにロック(基本的にはロールバックセグメントにあるトランザクションロックへのポインター)が更新されます。レコードに直接配置されます。

これは、レコードをOracleにロックすることは、レコードのメタデータを更新し、論理ページ書き込みを発行することを意味します。たとえば、読み取り専用のテーブルスペースではSELECT FOR UPDATEを実行できません。

それ以上に、レコード自体はコミット後に更新されません。代わりに、ロールバックセグメントが更新されます。

つまり、トランザクション自体が長い間死んでいたとしても、各レコードには、最後に更新されたトランザクションに関する情報が保持されます。トランザクションが有効であるかどうか(したがって、レコードが有効であるかどうか)を確認するには、ロールバックセグメントにアクセスする必要があります。

Oracleには従来のロックマネージャーがありません。つまり、すべてのロックのリストを取得するには、すべてのオブジェクトのすべてのレコードをスキャンする必要があります。これには時間がかかりすぎます。

ロックされたメタデータオブジェクト(v$locked_objectを使用)、ロック待機(v$sessionを使用)など、いくつかの特別なロックを取得できますが、データベース内のすべてのオブジェクトのすべてのロックのリストは取得できません。

12
Quassnoi

ロックについては、dba_blockersdba_waitersdba_locksをご覧ください。名前は自明でなければなりません。

たとえば、1分間に1回実行して、そのセッションのdba_blockersと現在アクティブなsql_idに値を記録するジョブを作成できます。 (v$sessionおよびv$sqlstatsを使用)。

また、v$sql_monitorを調べることもできます。これは、5秒以上かかるすべてのSQLのデフォルトのログになります。 Enterprise Managerの「SQL監視」ページにも表示されます。

5
PenFold

次のクエリでクエリすることにより、Oracleでロックされたテーブルを見つけることができます

    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
Baji Shaik

ロックではなく、v$transactionを使用して長時間実行トランザクションを確認することをお勧めします。そこからv$sessionに参加できます。これにより、UI(プログラムとマシンの列を試す)とユーザーについてのアイデアが得られます。

5
Gary Myers

次のPL/SQLブロックは、テーブル内のロックされたすべての行を検索します。他の答えは、ブロッキングsessionのみを検出し、実際にロックされたrowsを検出するには、各行を読み取ってテストする必要があります。

(ただし、おそらくこのコードを実行する必要はありません。ロックの問題が発生している場合は、通常、GV$SESSION.BLOCKING_SESSIONおよびその他の関連するデータディクショナリビューを使用して原因を特定する方が簡単です。これを実行する前に別の方法を試してくださいabysmally遅いコード。)

最初に、サンプルテーブルといくつかのデータを作成します。これをセッション#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'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;
/

最後に、LOCKED_ROWIDSテーブルに結合することで、ロックされた行を表示できます。

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


A
-
1
0
Jon Heller

いくつかのテーブルがあれば、どの行がSELECT FOR UPDATESKIP LOCKED でロックされているnotかを見つけることができます。

たとえば、次のクエリは、ロックされていないすべての行をロック(および返し)します。

SELECT * FROM mytable FOR UPDATE SKIP LOCKED

参照

0