注册
浅谈 ROWID 的组织机制及其在回表路径中的作用
专栏/技术分享/ 文章详情 /

浅谈 ROWID 的组织机制及其在回表路径中的作用

DM_045988 2026/04/17 117 3 0
摘要

一、ROWID 与普通表的默认聚集组织

达梦普通表(列存表和堆表除外)采用聚集索引组织方式存储数据。每个表有且仅有一个聚集索引;当建表时未显式指定聚集索引键,系统默认以 ROWID 作为聚集索引键,表中记录也因此按 ROWID 的顺序组织。由此可见,在普通表中,ROWID 并不只是一个用于标识记录的伪列,它同时还是表记录组织与访问路径的重要基础。

与之对应,达梦的非聚集索引,即二级索引,叶子节点中保存的是“二级索引列 + 聚集索引列”。这意味着,二级索引能够直接提供的并不是整行记录,而是索引键及其对应的聚集定位依据。当查询仅涉及索引列或聚集列时,可以直接从二级索引返回结果;而当查询需要访问其他列时,则必须依据叶子节点中保存的聚集索引列,回到一级索引继续定位整行记录,这就是回表。

因此,在 select * from ... 一类查询中,即使已经通过二级索引命中了目标记录,执行计划中仍然通常会出现 SSEK2BLKUP2 的组合:前者负责完成二级索引扫描,后者负责依据聚集定位信息回到一级索引取出完整记录。其根本原因在于,二级索引叶子节点并不保存整行数据,而只保存索引键与聚集索引列。换言之,二级索引叶子中携带的,本质上是指向 ROWID 聚集组织的定位依据;BLKUP2 的作用,也正是沿着这条定位链回到按 ROWID 组织的聚集结构中取出整行数据。

二、ROWID 的作用

在达梦数据库中,ROWID 的作用并不局限于“标识一条记录”。对于普通表而言,ROWID 同时承担了记录标识、表组织依据和访问定位锚点等多重职责。理解 ROWID 在数据库里的作用,关键不在于把它看成一个可查询的伪列,而在于把它放回到表的组织方式和访问路径中去观察。

首先,ROWID 参与普通表的默认聚集组织。达梦普通表在未显式指定聚集键时,默认以 ROWID 作为聚集索引键,表中记录按 ROWID 的顺序组织。因此,ROWID 不是一个脱离存储结构独立存在的附加属性,而是普通表聚集组织本身的一部分。数据库在组织、排序和访问表中记录时,天然以 ROWID 为基础。也正因为如此,ROWID 在普通表中既表示“这条记录是谁”,也参与决定“这条记录在表中如何被组织和访问”。

其次,ROWID 是二级索引回表时的重要定位依据。达梦二级索引叶子节点保存的是“二级索引列 + 聚集索引列”;而在默认普通表中,聚集索引列通常就是 ROWID。这意味着,二级索引命中后返回的并不是整行数据,而是索引键及其对应的聚集定位依据。当查询需要访问索引键以外的列时,执行器就必须依据这份定位依据回到一级索引继续查找整行记录。换句话说,ROWID 在这里的作用,是把“二级索引中的命中结果”和“表中的完整记录”连接起来,使二级索引能够进一步落到真实数据行。

再次,ROWID 还影响访问路径的代价。对于数据库而言,ROWID 不仅决定能否定位到目标记录,也影响定位过程需要经过怎样的路径。默认普通表中的许多访问过程,本质上都是围绕 ROWID 所代表的聚集组织展开的。特别是在二级索引命中但又不能覆盖访问的场景中,执行器需要借助 ROWID 或对应的聚集定位信息完成从索引记录到整行记录的转换。因此,ROWID 不仅参与访问,而且参与决定访问成本。

从更广的角度看,ROWID 还具有记录归属和组织分析的价值。ROWID 本身携带了记录在数据库组织体系中的编码信息,可以用来表示记录所属的站点、分区以及在表组织中的物理行号。也就是说,ROWID 不只是访问路径中的中间变量,还提供了一种观察记录归属、分布和组织方式的入口。对于理解表的默认聚集方式、分析访问路径以及进一步观察数据库内部组织而言,ROWID 都具有独立价值。

因此,ROWID 在数据库里的作用,可以概括为三层:它是记录的系统标识,是普通表默认聚集组织的基础,也是二级索引回表过程中的定位锚点。后文将结合实验,进一步验证这些作用在执行计划和访问代价上的具体体现。

三、验证

实验一:验证 ROWID 在默认普通表与显式 cluster 表中的组织作用

A. 默认普通表 t_rowid

步骤 1:创建默认普通表并写入数据

执行:

drop table t_rowid; create table t_rowid( id int, c1 varchar(20), c2 varchar(20) ); insert into t_rowid select level, dbms_random.string('U',20), dbms_random.string('U',20) from dual connect by level <= 100000; commit;

步骤 2:查询若干条记录的 ROWID

执行:

set autotrace off; select rowid, id from t_rowid where id between 100 and 110;

达梦 rowid(4.10)1c50d8a784374243899b8f6fd42204eb.png

记录其中一条 ROWID,例如:

AAAAAAAAAAAAAAABl

步骤 3:开启 autotrace 与执行监控

执行:

set autotrace traceonly; alter session set 'monitor_sql_exec' = 1;

步骤 4:按 ROWID 精确查询

执行:

select * from t_rowid where rowid = 'AAAAAAAAAAAAAAABl';

达梦 rowid(4.10)6648c50665be7cff9aa0eff0db14bd9a.png

实验现象

执行计划中出现 CSEK2,并直接对 t_rowid 的聚集索引进行定位,scan_range 为同一个 ROWID 常量;该次查询 rows processed = 1logical reads = 2physical reads = 0。这说明在默认普通表中,ROWID 条件被直接转化为了聚集访问路径上的定位条件。

B. 显式 cluster 表 t_clu

步骤 5:创建显式 cluster 表并写入数据

执行:

drop table t_clu; create table t_clu( id int not null, c1 varchar(20), c2 varchar(20), cluster primary key(id) ); set autotrace off; insert into t_clu select level, dbms_random.string('U',20), dbms_random.string('U',20) from dual connect by level <= 100000; commit;

步骤 6:查询若干条记录的 ROWID

执行:

set autotrace off; select rowid, id from t_clu where id between 100 and 110;

记录其中一条 ROWID

达梦 rowid(4.10)6a233419a89166d92c85714d29fcec29.png

步骤 7:开启 autotrace 与执行监控

执行:

set autotrace traceonly; alter session set 'monitor_sql_exec' = 1;

步骤 8:按 ROWID 精确查询

执行:

select * from t_clu where rowid = '记录下来的某个ROWID';

达梦 rowid(4.10)2fa3184146a8d2b244e01670a5b3b22e.png

实验现象

执行计划中未出现按 ROWID 直接定位的 CSEK2,而是表现为 CSCN2 + SLCT2CSCN2 负责聚集扫描,SLCT2 上的谓词为 T_CLU.ROWID = var1。该次查询 rows processed = 1logical reads = 439physical reads = 0。这说明在显式 cluster primary key(id) 的表中,ROWID 条件没有被转化为聚集访问键,而是作为扫描后的过滤条件存在。

C. 整理对照结论

同样是 where rowid = 常量 的等值查询,默认普通表 t_rowid 上表现为 CSEK2 直接定位,逻辑读仅为 2;显式 cluster 表 t_clu 上则表现为 CSCN2 + SLCT2,逻辑读上升到 439。这个对照说明:在默认普通表 t_rowid 上,ROWID 作为默认聚集索引键,where rowid = 已知量 可直接走 CSEK2 聚集索引定位;而在显式指定 cluster primary key(id)t_clu 上,聚集索引键已变为 idROWID 虽然仍可标识记录,但已经不再是该表的聚集访问键,因此执行计划退化为 CSCN2 聚集索引扫描配合 SLCT2 过滤。两者的执行计划差异,正体现了 ROWID 在默认表中是聚集组织键、而在显式 cluster 表中只是记录属性而非聚集访问键的本质区别。


实验二:验证 WITH CLU_REC_ADDR 对回表路径的优化效果

步骤 1:准备测试表和数据

执行:

drop table test1; create table test1(id int,c1 varchar(20),c2 varchar(20)); truncate table test1; insert into test1 select level, dbms_random.string('U',20), dbms_random.string('U',20) from dual connect by level <= 100000; commit;

步骤 2:创建带地址信息的二级索引

执行:

create or replace index idx_dmtest1_c1 on test1(c1) with clu_rec_addr;

步骤 3:开启执行监控与 autotrace

执行:

set autotrace traceonly; alter session set 'monitor_sql_exec' = 1;

步骤 4:执行查询

执行:

select /*+plan_op_flag(8)*/* from test1 where c1 > '0';

达梦 rowid(4.10)image.png

步骤 5:重建成普通索引

执行:

create or replace index idx_dmtest1_c1 on test1(c1);

步骤 6:再次执行同一条 SQL

执行:

select * from test1 where c1 > '0';

达梦 rowid(4.10)image1.png

步骤 7:整理对照结论

两次执行,逻辑读的次数相差了两个数量级。这说明,WITH CLU_REC_ADDR 带来的收益并不只是少一次“通过逻辑 ROWID 再定位聚集记录”的查找。因为如果收益仅来自这一步的消除,那么访问代价虽然会因减少定位聚集索引内记录而下降,但难以单独解释当前测例中两个数量级的差异。

达梦 rowid(4.10)image2.png

更合理的解释是,二级索引在额外带上聚集记录地址/页地址后,执行器在回表阶段不再是逐条按聚集键重复定位记录,能够按更有利于物理页访问的方式组织目标记录访问,使同页或相邻页上的记录更集中地完成读取,从而显著降低回表阶段的逻辑读。在DM里,物理 ROWID 的形式是通过文件号、页号和页内偏移直接生成该值,不需要关联逻辑 ROWID 值。倘若二级索引外带上聚集记录地址/页地址,当通过二级索引获得大量物理 ROWID 时,可以不经过排序地合并页号相同的记录,从而读一页可以获得多条记录,不需要每一条记录都单独进行一次寻址操作,让BLKUP2 阶段的定位与访问成本进一步大幅下降。通过这种方式,可以把降低逻辑读的比例从三分之一再下探到两个数量级。

达梦 rowid(4.10)image3.png

四、结论

  1. 这是一种“回表操作符级优化”,不是“计划消除型优化”

《DM8 系统管理员手册:管理索引》里面写道:二级索引叶子节点保存的是二级索引列和聚集索引列,查询其他列时必须回聚集索引查找;BLKUP2 正是这个过程的执行体现。WITH CLU_REC_ADDR 对应的 use_clu_addr 能力,优化的就是“从二级索引记录定位聚集记录”的过程。因此,这项能力的本质不是“把 BLKUP2 去掉”,而是“让 BLKUP2 更便宜”。

  • 它最适合解决“回表无法消除,但成本太大”的场景

《DM8 系统管理员手册:SQL 调优》里提到,可以通过覆盖索引直接避免 BLKUP2 的二次 IO;但覆盖索引会显著增大索引体积,需要权衡。WITH CLU_REC_ADDR 则适用于另一类场景:SQL 结果列多、业务查询复杂、无法轻易改成覆盖索引,但又必须控制回表逻辑读。这类场景正是它的价值所在。

从机制上看,这项能力更像是在现有表组织方式不变的前提下,对默认回表路径做减法。表仍然按原有聚集方式组织,二级索引仍然负责按业务键命中记录,BLKUP2 也仍然存在;真正变化的是,二级索引记录中额外保留了更利于定位聚集记录的地址信息,使得执行器在回表阶段可以减少重复定位带来的逻辑读和访问代价。因此,它的价值不在于“换了一种计划”,而在于即使计划主形态不变,仍然能够通过缩短回表链路获得收益。

五、ROWID 的扩展用途与分析价值

在达梦数据库中,ROWID 并不是一个仅用于语法层访问的伪列,也不是一个可以简单等同为“当前物理地址”的内部编号。它同时承担了记录标识、表组织、访问路径定位与分析入口等多重角色。理解 ROWID,需要将“记录身份”“聚集组织”“回表定位”以及“物理分布观察”几个层面放在同一框架内考察。

ROWID 与 PHYROWID 的区别

讨论 ROWID 时,需要先区分 ROWIDPHYROWIDROWID 公开表达的是站点号、分区号和物理行号这套编码信息;PHYROWID 则用于表示当前记录的物理存储信息,其内容由物理记录的文件号、页号和页内槽号组成。官方进一步指出,PHYROWID 的含义还与实际访问路径相关:当语句执行中使用 CSCNCSEKBLKUP 等聚集 B 树相关操作符时,PHYROWID 反映的是聚集 B 树中的物理地址;当语句仅使用 SSEKSSCN 等二级索引相关操作符时,PHYROWID 反映的是二级 B 树中的物理地址。

这一划分意味着,ROWID 更偏向于“记录在表组织体系中的身份坐标”,而 PHYROWID 更偏向于“当前访问路径下记录具体落在哪个文件、哪一页、哪一个槽位”。如果不区分这两个层次,就容易把行标识、聚集组织键与当前物理页地址混为一谈,从而在后续讨论回表、页分布或空间膨胀时发生概念错位。

值得注意的是,达梦堆表采用的是PHYROWID。官方文档明确说明,堆表的 PHYROWID 由文件号、页号和页内偏移直接生成,因此一旦知道堆表记录的 ROWID,就可以直接定位到该条记录的物理落点。

ROWID 携带的信息

从编码结构看,ROWID 将一条记录的定位信息拆分成了三个维度:站点号对应记录所属站点,分区号对应记录所属分区,物理行号对应记录在表组织中的物理行编号。达梦 SQL 附录提供了一组与 ROWID 直接相关的系统函数,例如 SF_BUILD_ROWID 用于根据站点号、分区号和物理行号构造 ROWIDSF_ROWID_GET_EP_SEQNO 用于获取站点号,SF_ROWID_GET_PARTNO 用于获取分区号,SF_GET_REAL_ROWID 用于获取物理行号。由此可见,ROWID 并不是一个不可解释的黑箱值,而是一种可构造、可拆解、可分析的系统标识。

这种编码方式的意义在于,它把“记录是谁”“记录属于哪里”“记录在当前组织中位于什么位置”转化为可以被程序和运维工具直接读取的信息。在单机环境下,站点号通常为零值即AAAA;如果是非分区表,则 6 位分区号为0即 AAAAAA。在分区表、DSC、MPP 或 DPC 等更复杂的环境中,这些字段则直接承担了记录来源与归属的表达功能。对于定位问题、分区归属验证、跨站点排查以及系统级数据诊断而言,这类信息具有直接价值。

ROWID 的用途

ROWID 的最直接用途,是对已经找到的记录进行精确重访。官方文档里给出的典型模式是:查询时同时取出 ROWID,后续更新语句再通过 ROWID 精确访问目标行。若查询时使用 FOR UPDATE,则该行会被锁定,从而保证查询和后续更新之间的一致性。这一能力适用于精确修数、无业务主键表的单行更新,以及“先查后改”的稳定定位场景。

ROWID 的第二类用途,是支撑表访问路径中的“回表”机制。达梦官方索引文档指出,非聚集索引(二级索引)的叶子节点共同存储“二级索引列 + 聚集索引列”;如果查询只涉及二级索引列或聚集索引列,索引层即可直接返回;如果查询还需要其他列,则必须回到一级索引继续查找。由于普通表默认聚集索引键通常为 ROWID,因此二级索引命中后返回聚集索引进一步取整行,本质上就是围绕 ROWID 展开的定位过程。

进一步地,ROWID 还进入了优化器的代价模型。SQL查询优化文档明确指出,二级索引扫描的代价不仅依赖于 B 树层次和需扫描的叶子块数,还依赖于“根据 ROWID 访问聚集索引的记录数”。这意味着,ROWID 在执行层不仅是回表的技术细节,而且直接参与了访问代价的估算。在执行计划说明中,BLKUP2use_clu_addr 属性表示“是否从二级索引记录读取对应的聚集索引记录地址对定位聚集索引记录进行优化”,从而进一步说明了 ROWID 及其相关地址信息在回表优化中的核心地位。

ROWID 的第三类用途,是在无主键或不依赖主键的场景中承担增量定位角色。达梦物化视图文档表明,快速刷新既可以基于主键,也可以基于 ROWID;对于 WITH ROWID 的快速刷新,单表场景下物化视图日志必须包含 ROWID,多表场景下每张基表的日志都必须包含 ROWID,并且查询定义中需要逐一选择 ROWID 并给出别名。这说明 ROWID 不只是单表内部的访问信息,还可以成为增量刷新与数据同步机制中的定位。

ROWID 的第四类用途,是在日志挖掘与变更重构中标识目标行。达梦 DBMS_LOGMNR 文档显示,NO_ROWID_IN_STMT 选项用于控制拼写 SQL 时是否包含 ROWID。这意味着在日志挖掘默认语境下,ROWID 是重构 DML 目标行的重要线索之一;也正因此,关闭 ROWID 输出需要被作为显式选项处理。对于审计、恢复、差异分析与日志级排障而言,这一能力具有明显意义。

ROWID 的第五类用途,则体现在更底层的物理分布分析上。这里主角已经不再是抽象的 ROWID 编码,而是 ROWID 体系中的 PHYROWID。借助 PHYROWID 中的文件号、页号和槽号,可以把“某条记录位于哪一页”这一事实显式化;再结合 SF_GET_ROWID_BY_PAGE(ts_id, file_id, page_no) 这类页级函数,可以从页反向获取记录集合,从而开展页分布、热点页、稀疏度、回表离散度、碎片化与空间膨胀等分析。

一组 ROWID 包含的信息

1. 可以统计站点分布、分区分布与逻辑归属分布

达梦公开给出的 ROWID 结构是“站点号 + 分区号 + 物理行号”,并提供了 SF_ROWID_GET_EP_SEQNOSF_ROWID_GET_PARTNOSF_GET_REAL_ROWID 等函数用于拆解。因此,多个 ROWID 放在一起,最直接可以做的是:统计记录来自哪些站点、属于哪些分区、各分区占比如何、某类业务数据是否集中在特定分区。这类统计并不反映页级物理位置,但非常适合做逻辑归属分析、分区落点验证和分布式环境下的数据来源分析。

2. 可以分析记录在普通表组织中的顺序特征

达梦普通表在未显式指定聚集索引键时,默认聚集索引键是 ROWID;官方文档同时说明,在这种组织方式下,ROWID 是 B 树为记录生成的逻辑递增序号。这意味着,在普通表语境下,多个 ROWID 可以用来观察记录在聚集组织中的先后关系、范围分布以及某些批次数据是否集中在相近的 ROWID 区间。

3. 可以验证二级索引回表的逻辑归并程度

达梦二级索引叶子节点存储的是“二级索引列 + 聚集索引列”;若查询列不在二级索引中,就需要再根据 ROWID 或聚集索引定位到数据行。由此可知,多个 ROWID 还可以用来分析某次查询命中的结果集在聚集组织上是否集中:若命中的 ROWID 范围较连续,通常说明回表时更容易具备局部性;若 ROWID 分布高度离散,则意味着命中的逻辑落点更分散。它提供了一个重要视角:查询命中的记录,在表的逻辑组织层面是集中还是分散

4. 可以为无主键场景提供增量定位与集合比对依据

达梦支持基于 ROWID 的物化视图与快速刷新;官方要求在 WITH ROWID 的快速刷新场景中,查询定义中必须显式选择所有相关 ROWID 列并给出别名。这说明多个 ROWID 还可以用作集合级比较与增量定位的键:例如在无主键表中,用一组 ROWID 表示“这一批被追踪的记录”,或者在刷新与同步过程中,用 ROWID 作为变化集合的定位依据。因此,多个 ROWID 可以做站点/分区分布统计、逻辑区间分布分析、回表逻辑集中度分析,以及无主键场景下的集合定位

一组 PHYROWID 包含的信息

1. 可以统计页分布与页内记录数分布

这是最直接、也是最可靠的用途。因为 PHYROWID 公开对应文件号、页号、页内槽号,所以把多个 PHYROWID 放在一起,至少可以把记录按“文件号 + 页号”归并,得到“哪些记录落在同一页”。一旦完成这种归并,就可以继续统计每页记录数的分布,例如平均每页多少行、中位数多少行、众数是多少行、最稀疏页与最密集页分别是什么状态,也就是页利用状态的分布。这个结论不依赖对页大小的反推,而是直接建立在 PHYROWID 的定位语义上。

2. 可以分析页离散度、聚簇性与访问局部性

当多个 PHYROWID 与某种业务顺序结合起来时,例如按主键顺序、按插入顺序或按某个查询结果顺序观察,就可以进一步分析记录在物理页上的连续性。若相邻业务记录对应的 PHYROWID 经常落在相邻页或同一页,说明物理局部性较好;若这些记录频繁跳页,则说明物理离散度较高。由于达梦索引扫描和回表都与“根据 ROWID 或聚集索引继续找到数据行”有关,页离散度越高,通常意味着逻辑读与物理读的局部性越差。

3. 可以在已知页大小后,进一步估算页填充率与膨胀率

PHYROWID 自身不能推出页大小,页大小必须从实例参数单独获取,例如 select page/1024; 或查询 GLOBAL_PAGE_SIZE。但是一旦页大小已知,多个 PHYROWID 就可以与对象实际占用页数结合起来,进一步估算页填充率、页利用率与膨胀率。第一步,依据 PHYROWID 将记录按页归并,得到每页记录数分布。第二步,依据对象空间统计函数获取表或索引实际占用页数。第三步,将“理论每页可容纳记录数”与“实际每页记录数分布”对照,或将现状对象与重建后的紧凑对象对照,由此求出页利用率下降幅度与膨胀倍数。这里的“理论容量”来自页大小与行长模型,而不是来自 PHYROWID 本身。

4. 可以做页级反查与页热点定位

达梦还提供了按页反查 ROWID 的系统函数 SF_GET_ROWID_BY_PAGE(ts_id, file_id, page_no)。这说明页并不是一个只能通过底层工具观察的概念,而是可以通过 ROWID 体系进行程序化分析。若配合多个 PHYROWID 的分布统计,就可以定位出记录集中分布的热点页、过于稀疏的低效页,以及某一类业务数据主要落在哪些文件与页区间。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服