如何在Oracle中查找锁定的行

时间:2010-02-02 19:09:40

标签: oracle locking

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

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

我们有理由相信它可能是一个行为不端的用户界面,但却找不到“吸烟枪”。

我发现了一些列出块的查询,但是当你有两个工作争夺一行时就是这样。我想知道当没有第二个工作试图锁定时哪些行有锁。

我们是11g,但自8i以来一直遇到这个问题。

6 个答案:

答案 0 :(得分:12)

Oracle的锁定概念与其他系统的概念完全不同。

Oracle中的一行被锁定时,记录本身会更新为新值(如果有的话),另外还有一个锁(它实际上是一个指向驻留在回滚段中的事务锁的指针) )被放置在记录中。

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

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

这意味着每条记录都包含有关上次更新的事务的一些信息,即使事务本身早已死亡。要确定事务是否存在(因此,如果记录是否存活),则需要访问回滚段。

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

您可以获取一些特殊的锁,例如锁定的元数据对象(使用v$locked_object),锁定等待(使用v$session)等,但不能获取数据库中所有对象的所有锁定列表。< / p>

答案 1 :(得分:6)

查看dba_blockers,dba_waiters和dba_locks以进行锁定。这些名字应该是自我解释的。

您可以创建一个每分钟运行一次的作业,并记录dba_blockers中的值以及该会话的当前活动sql_id。 (通过v $ session和v $ sqlstats)。

您可能还想查看v $ sql_monitor。这将是默认日志所有需要超过5秒的SQL。它也可以在Enterprise Manager的SQL Monitoring页面上看到。

答案 2 :(得分:5)

我建议您使用v$transaction查看长时间运行的事务,而不是锁定。从那里你可以加入v$session,它可以让你了解用户界面(尝试程序和机器列)以及用户。

答案 3 :(得分:3)

您可以通过查询以下查询

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

答案 4 :(得分:0)

下面的代码将找到表中所有锁定的行。

(但是,您可能不需要运行此代码。如果遇到锁定问题,通常更容易使用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

答案 5 :(得分:0)

给出一些表,您可以找到用SELECT FOR UPDATE SKIP LOCKED锁定的行 not

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

SELECT * FROM mytable FOR UPDATE SKIP LOCKED

参考