如何在Oracle中查找锁定行

如何在Oracle中查找锁定行,oracle,locking,Oracle,Locking,我们有一个Oracle数据库,customer account表有大约一百万行。多年来,我们构建了四个不同的UI(两个在Oracle表单中,两个在.Net中),所有这些都在使用中。我们也有一些后台任务(持久任务和计划任务) 有时会有东西在account表中的某一行上持有长锁(例如,超过30秒),这会导致一个持久性后台任务失败。更新超时后,相关后台任务将重新启动。我们在它发生几分钟后发现了它,但到那时锁已经被释放了 我们有理由相信这可能是一个行为不端的用户界面,但还没有找到“冒烟的枪” 我发现一些

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

有时会有东西在account表中的某一行上持有长锁(例如,超过30秒),这会导致一个持久性后台任务失败。更新超时后,相关后台任务将重新启动。我们在它发生几分钟后发现了它,但到那时锁已经被释放了

我们有理由相信这可能是一个行为不端的用户界面,但还没有找到“冒烟的枪”

我发现一些查询会列出块,但这是针对两个工作竞争一行的情况。我想知道当不一定有第二个作业试图获取锁时,哪些行有锁


我们使用的是11g,但从8i开始就遇到了问题。

Oracle的锁定概念与其他系统的锁定概念大不相同

Oracle
中的一行被锁定时,记录本身将被更新为新值(如果有),此外,一个锁(本质上是指向驻留在回滚段中的事务锁的指针)将被直接放置到记录中

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

除此之外,提交后记录本身不会更新:相反,回滚段会更新

这意味着,每个记录都保存有关上次更新它的事务的一些信息,即使事务本身早就死了。要确定事务是否处于活动状态(因此,记录是否处于活动状态),需要访问回滚段

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


您可以获得一些特殊的锁,如锁定的元数据对象(使用
v$locked\u object
),锁等待(使用
v$session
)等,但不能获得数据库中所有对象上所有锁的列表。

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

查看用于锁定的
dba_阻止程序
dba_等待程序
dba_锁
。这些名字应该是不言自明的

您可以创建一个作业,例如每分钟运行一次,并记录该会话的
dba\u blockers
中的值和当前活动的
sql\u id
。(通过
v$session
v$sqlstats


您可能还需要查看
v$sql\u监视器
。这将是花费5秒以上的默认日志所有SQL。在Enterprise Manager的“SQL Monitoring”页面上也可以看到它。

您可以通过以下查询在oralce中找到锁定的表

    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;

下面的PL/SQL块查找表中所有锁定的行。其他答案只找到阻塞会话,找到实际锁定的行需要读取和测试每一行

(但是,您可能不需要运行此代码。如果您有锁定问题,通常使用
GV$SESSION.BLOCKING_SESSION
和其他相关的数据字典视图更容易找到罪犯。请在运行此极其缓慢的代码之前尝试另一种方法。)

首先,让我们创建一个示例表和一些数据。在会话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_锁定到您自己的表

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

给定一些表,您可以找到哪些行未使用
选择更新
锁定

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

SELECT * FROM mytable FOR UPDATE SKIP LOCKED
参考资料


dba_blockers
和您提到的其他视图将只列出等待的锁@op指定他想要所有的锁,不仅仅是被争用记录上的锁。但是如果没有任何东西被阻止,那么会话可以锁定任意时间,因为它不会影响性能。只有阻塞锁会影响性能。您不能使用V$LOCK获取所有锁吗?即使您无法确定要锁定的特定行,至少您知道哪些表。要执行此查询,需要DBA管理员权限吗?获取无效的视图或表
SELECT * FROM mytable FOR UPDATE SKIP LOCKED