【问题标题】:How to find locked rows in Oracle如何在Oracle中查找锁定的行
【发布时间】:2019-12-22 21:38:25
【问题描述】:

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

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

我们有理由相信它可能是一个行为不端的用户界面,但一直找不到“确凿证据”。

我发现了一些列出块的查询,但那是在您有两个工作竞争一行的情况下使用的。当不一定有第二份工作试图获得锁时,我想知道哪些行有锁。

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

【问题讨论】:

    标签: oracle locking


    【解决方案1】:

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

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

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

    不仅如此,提交后记录本身不会更新:而是更新回滚段。

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

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

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

    【讨论】:

    • 不能'你使用V$LOCK来获得所有的锁吗?即使你无法确定被锁定的具体行,至少你知道哪些表。
    【解决方案2】:

    你可以通过以下查询在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;
    

    【讨论】:

    • 执行此查询需要 DBA 管理员权限吗?获取无效的视图或表格
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      查看dba_blockersdba_waitersdba_locks 进行锁定。名称应该是不言自明的。

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

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

      【讨论】:

      • dba_blockers 和您提到的其他视图只会列出正在等待的锁。 @op 指定他想要所有锁,而不仅仅是那些正在竞争的记录上的锁。
      • 但是如果没有任何东西被阻塞,那么会话可以锁定只要它喜欢,因为它不会影响性能。只有阻塞锁会影响性能。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        给定一些表,您可以找到哪些行SELECT FOR UPDATESKIP LOCKED锁定。

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

        SELECT * FROM mytable FOR UPDATE SKIP LOCKED
        

        参考文献

        【讨论】:

          猜你喜欢
          • 2016-04-18
          • 1970-01-01
          • 2017-05-02
          • 1970-01-01
          • 1970-01-01
          • 2018-08-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多