ITPub博客

首页 > 数据库 > Oracle > 如何从等待事件定位到具体数据块和具体行

如何从等待事件定位到具体数据块和具体行

原创 Oracle 作者:a960549548 时间:2026-09-11 15:28:12 0 删除 编辑

排查 I/O 相关的性能问题时,等待事件给出的信息是物理地址: db file sequential read 的 P1 是文件号、 P2 是块号, buffer busy waits 等待同样以文件号加块号标识热点块。 DBA 拿到这组数字后,下一步往往就断了线,这是哪个表的哪个段落?如果要继续追,能不能定位到具体行?

这一步的关键是 ROWID ,以及配套的 DBMS_ROWID 包。 ROWID 本身就是行的物理地址,扩展 ROWID 由 18 个 base64 字符构成,从左到右依次是 6 位数据对象号、 3 位相对文件号、 6 位块号、 3 位行槽号。 DBMS_ROWID 负责这串字符与四段数值之间的双向换算,是纯粹的分析型工具, EXECUTE 权限同样授予了 PUBLIC 。

一、包结构

SQL> desc dbms_rowid
FUNCTION ROWID_BLOCK_NUMBER RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROW_ID                         ROWID                   IN
 TS_TYPE_IN                     VARCHAR2                IN     DEFAULT
FUNCTION ROWID_CREATE RETURNS ROWID
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROWID_TYPE                     NUMBER                  IN
 OBJECT_NUMBER                 NUMBER                  IN
 RELATIVE_FNO                   NUMBER                  IN
 BLOCK_NUMBER                   NUMBER                  IN
 ROW_NUMBER                     NUMBER                  IN
PROCEDURE ROWID_INFO
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROWID_IN                       ROWID                   IN
 ROWID_TYPE                     NUMBER                  OUT
 OBJECT_NUMBER                  NUMBER                  OUT
 RELATIVE_FNO                   NUMBER                  OUT
 BLOCK_NUMBER                   NUMBER                  OUT
 ROW_NUMBER                     NUMBER                  OUT
FUNCTION ROWID_OBJECT RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROWID_ID                       ROWID                   IN
FUNCTION ROWID_RELATIVE_FNO RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROWID_ID                       ROWID                   IN
 TS_TYPE_IN                     VARCHAR2                IN     DEFAULT
FUNCTION ROWID_ROW_NUMBER RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ROW_ID                         ROWID                   IN
子程序 作用
ROWID_OBJECT 从 ROWID 中取出数据对象号
ROWID_RELATIVE_FNO / ROWID_BLOCK_NUMBER / ROWID_ROW_NUMBER 取出相对文件号、块号、行槽号,可直接在 SQL 中按行调用
ROWID_INFO 一次取出全部四段信息,过程,只能在 PL/SQL 中使用
ROWID_CREATE 按四段数值反向构造 ROWID,rowid_type 传 1 表示扩展 ROWID、0 表示老式受限 ROWID

二、实测:分解与还原 ROWID

1. 准备数据(TEST 用户)
SQL> create table t_rowid(id number, pad varchar2(100));
 
Table created.
 
SQL> begin
  2    for i in 1 .. 10000 loop
  3      insert into t_rowid values (i, rpad('x', 100, 'x'));
  4    end loop;
  5    commit;
  6  end;
  7  /
 
PL/SQL procedure successfully completed.
2. 查看每一行的物理位置
SQL> select id,
  2         dbms_rowid.rowid_object(rowid)       obj#,
  3         dbms_rowid.rowid_relative_fno(rowid) file#,
  4         dbms_rowid.rowid_block_number(rowid) block#,
  5         dbms_rowid.rowid_row_number(rowid)   row#
  6  from t_rowid
  7  where rownum <= 5;
 
        ID       OBJ#      FILE#     BLOCK#       ROW#
---------- ---------- ---------- ---------- ----------
         1      74598          5        178          0
         2      74598          5        178          1
         3      74598          5        178          2
         4      74598          5        178          3
         5      74598          5        178          4
同一块内的行块号相同、行槽号递增,规律一目了然。
 
3. 在 PL/SQL 中用 ROWID_INFO 一次取全
SQL> set serveroutput on
SQL> declare
  2    v_type number; v_obj number; v_fno number; v_bno number; v_rno number;
  3  begin
  4    dbms_rowid.rowid_info(
  5      (select max(rowid) from t_rowid),
  6      v_type, v_obj, v_fno, v_bno, v_rno);
  7    dbms_output.put_line('type=' || v_type || ' obj=' || v_obj ||
  8                          ' file=' || v_fno || ' block=' || v_bno ||
  9                          ' row=' || v_rno);
 10  end;
 11  /
type=1 obj=74598 file=5 block=326 row=41
 
PL/SQL procedure successfully completed.
4. 反向构造,验证往返一致
SQL> select count(*) from t_rowid
  2  where rowid = dbms_rowid.rowid_create(1,
  3         dbms_rowid.rowid_object(rowid),
  4         dbms_rowid.rowid_relative_fno(rowid),
  5         dbms_rowid.rowid_block_number(rowid),
  6         dbms_rowid.rowid_row_number(rowid));
 
  COUNT(*)
----------
     10000

分解后再构造,仍能匹配回全部 10000 行,说明换算是无损的。 ROWID_CREATE 的第一个参数 1 即扩展 ROWID ;官方文档明确说明该函数用于测试目的,构造出的 ROWID 若指向不存在的位置,查询时可能返回空或报 ORA-1410 (非法 ROWID )、 ORA-28516 (块超出文件末尾)。

三、实测:统计每个块的行数

结合 GROUP BY,可以观察数据在块上的分布:
SQL> select * from (
  2    select dbms_rowid.rowid_relative_fno(rowid) file#,
  3           dbms_rowid.rowid_block_number(rowid) block#,
  4           count(*) rows_in_block
  5    from t_rowid
  6    group by dbms_rowid.rowid_relative_fno(rowid),
  7             dbms_rowid.rowid_block_number(rowid)
  8    order by count(*) desc
  9  ) where rownum <= 5;
 
     FILE#     BLOCK# ROWS_IN_BLOCK
---------- ---------- -------------
         5        178            66
         5        179            66
         5        180            66
         5        181            66
         5        182            66

如果怀疑热点块竞争( V$SEGMENT_STATISTICS 中某段的 BUFFER_BUSY_WAITS 偏高),这个查询可以进一步定位竞争集中在哪些块、这些块上是什么数据。反过来,对已知的行执行分解,也能得到它所在的块。

四、实测:从等待事件定位对象

演示从 db file sequential read 的参数走完整流程。先找到等待中的会话(DBA):
SQL> select sid, event, p1, p2
  2  from v$session_wait
  3  where event = 'db file sequential read';
 
       SID EVENT                      P1         P2
---------- -------------------- ---------- ----------
       210 db file sequential read       5        326
P1 为文件号、P2 为块号,用 DBA_EXTENTS 反查该块所属的段:
 
SQL> select owner, segment_name, segment_type, tablespace_name
  2  from dba_extents
  3  where file_id = 5
  4    and 326 between block_id and block_id + blocks - 1;
 
OWNER  SEGMENT_NAME    SEGMENT_TYPE       TABLESPACE_NAME
------ --------------- ------------------ --------------------
TEST   T_ROWID         TABLE              USERS
到这里物理地址就落到了具体对象。若还想知道块里存了哪些行,可以在该表上按块范围扫描:
SQL> select id from t_rowid
  2  where dbms_rowid.rowid_relative_fno(rowid) = 5
  3    and dbms_rowid.rowid_block_number(rowid) = 326;
 
        ID
----------
      9953
      9954
      9955
...
也可以用 ROWID_CREATE 构造出指定块内某个行槽的 ROWID 后直接按 ROWID 访问,思路相同。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013625/viewspace-3182199/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle ACE,MySQL 8.0 OCP,PMP,拥有OceanBase OBCP,TiDB PCTP,GoldenDB GDCP等多项认证,ITPUB认证专家,微信公众号:DBA之友。

注册时间:2022-02-08

  • 博文量
    132
  • 访问量
    171482