如何在Oracle中查找锁定的行

问题描述 投票:0回答:6

我们有一个Oracle数据库,客户账户表大约有一百万行。多年来,我们构建了四种不同的 UI(两种在 Oracle Forms 中,两种在 .Net 中),所有这些都仍在使用中。我们还有许多后台任务(持久的和计划的)。

某些东西偶尔会在帐户表中的一行上持有长时间锁定(例如超过 30 秒),这会导致其中一个持久后台任务失败。一旦更新超时,相关后台任务就会自行重新启动。我们在事情发生几分钟后才发现,但那时锁已经被释放了。

我们有理由相信这可能是一个行为不当的 UI,但还没有找到“确凿的证据”。

我发现了一些列出块的查询,但这是当你有两个作业争夺一行时的情况。我想知道当不一定有第二个作业尝试获取锁时哪些行有锁。

我们使用的是 11g,但自 8i 以来一直遇到此问题。

oracle locking
6个回答
13
投票

Oracle
的锁定概念与其他系统有很大不同。

Oracle
中的一行被锁定时,记录本身会使用新值(如果有)进行更新,此外,还会在右侧放置一个锁(本质上是指向驻留在回滚段中的事务锁的指针)记入记录。

这意味着锁定

Oracle
中的记录意味着更新记录的元数据并发出逻辑页写入。例如,您不能在只读表空间上执行
SELECT FOR UPDATE

更重要的是,提交后记录本身不会更新:而是更新回滚段。

这意味着每条记录都保存了有关最后更新它的事务的一些信息,即使事务本身早已死亡。要查明事务是否处于活动状态(以及记录是否处于活动状态),需要访问回滚段。

Oracle没有传统的锁管理器,这意味着获取所有锁的列表需要扫描所有对象中的所有记录。这会花费太长时间。

您可以获得一些特殊的锁,例如锁定元数据对象(使用

v$locked_object
)、锁等待(使用
v$session
)等,但不是数据库中所有对象上的所有锁的列表。


7
投票

您可以通过以下查询找到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
投票

我建议您使用 v$transaction 来查看

长时间运行的事务
,而不是锁。从那里您可以加入
v$session
,这应该能让您了解 UI(尝试程序和机器列)以及用户。


5
投票

查看

dba_blockers
dba_waiters
dba_locks
进行锁定。名称应该是不言自明的。

您可以创建一个作业,例如每分钟运行一次,并记录该会话的

dba_blockers
中的值以及当前活动的
sql_id
中的值。 (通过
v$session
v$sqlstats
)。

您可能还想查看

v$sql_monitor
。这将默认记录所有耗时超过 5 秒的 SQL。它也可以在企业管理器的“SQL 监控”页面上看到。


0
投票

下面的 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'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
投票

给定一些表,您可以找到哪些行被SELECT FOR UPDATE

SKIP LOCKED
锁定。
例如,此查询将锁定(并返回)每个未锁定的行:

SELECT * FROM mytable FOR UPDATE SKIP LOCKED

参考文献

    询问 TOM“如何获取 Oracle 中锁定行的 ROWID”
© www.soinside.com 2019 - 2024. All rights reserved.