注册
如何高效通过表名查询数据库物理页号及其应用场景
专栏/培训园地/ 文章详情 /

如何高效通过表名查询数据库物理页号及其应用场景

Ariamaru 2026/08/06 14 0 0
摘要

如何高效通过表名查询数据库物理页号及其应用场景(避开 V$SEGMENTINFO 性能陷阱)

在日常运维中,我们常需获取一张表的实际数据页号(PAGE_NO)、段号、簇号等信息,用于热点分析、存储分布排查等操作。借助动态视图 V$SEGMENTINFO通常能获取完全段号、簇号、数据页号等信息,但该视图存在严重的性能隐患——每次查询都会全量扫描所有数据文件,对于生产大库而言基本不可用,同时还有条件限制,本文将指出这一弊端,并给出一个稳定、简洁的替代方案。

一、通过V$SEGMENTINFO获取PAGE_NO

达梦的表(列存储表和堆表除外)都是索引组织表,而通常都有一个聚集索引,当你创建一个表,并且没有显式指定聚集索引键时,达梦数据库会自动选择ROWID作为聚集索引键。

对于索引组织表,数据是存在LEAF_SEG叶子节点段上的,所以可以根据v$segmentinfo获取对应聚集索引信息

image.png

步骤一:查询表的聚集索引ID

通过系统视图dba_indexesdba_objects,根据表名和模式名定位聚集索引。聚集索引的INDEX_TYPECLUSTER,且索引名通常以INDEX开头(达梦自动生成的聚集索引命名规则)。

SELECT OBJECT_ID FROM dba_indexes a, dba_objects b WHERE TABLE_NAME = '表名' AND a.INDEX_NAME = b.OBJECT_NAME AND b.owner = '模式名' AND INDEX_TYPE = 'CLUSTER' AND INDEX_NAME LIKE 'INDEX%';

image.png

步骤二:通过聚集索引ID获取数据页号

查询动态视图v$segmentinfo,其中INDEX_ID等于上一步获取的索引ID,即可获得该索引对应的段号和物理页号。

SELECT * FROM v$segmentinfo WHERE index_id = ( SELECT OBJECT_ID FROM dba_indexes a, dba_objects b WHERE TABLE_NAME = '表名' AND a.INDEX_NAME = b.OBJECT_NAME AND b.owner = '模式名' AND INDEX_TYPE = 'CLUSTER' AND INDEX_NAME LIKE 'INDEX%' );

image.png

有了段号,查对应的簇号也是很简单的:

image.png


该方法的弊端

1、该方法只适用于普通表(索引组织表),对于堆表列存储表不适用。

例如创建一个堆表,去查询的时候就卡在第一步了,查不到OBJECT_ID

image.png

2、由于v$segmentinfo这个视图每次查的时候,都要全扫数据文件,如果全部数据文件扫一遍,对大库来说基本就是不可用的。

3、灵活性不够高,无法具体到某一条数据的物理页号所在,并且查询视图时,只能用等值条件,无法用范围查询WHERE seg_id > 100。

二、高效方案:通过PHYROWID查找

伪列 PHYROWID 用来表示当前记录的物理存储信息。

PHYROWID 值由聚集 B 树或二级 B 树中物理记录的文件号、页号、页内槽号组成,能体现聚集 B 树或二级 B 树的存储信息,聚集 B 树记录的最高位为 1。

当查询语句中实际使用 CSCN、CSEK、BLKUP 操作符时,PHYROWID 内容是聚集 B 树中记录的物理存储地址;当查询语句中实际仅使用 SSEK、SSCN 操作符时,PHYROWID 内容是二级 B 树中记录的物理存储地址。

字节偏移量 长度(字节) 存储内容 说明
0 ~ 3 4 TSK_ID 表空间密钥(Table Space Key),用于区分不同的数据文件/表空间
4 ~ 11 8 PAGE_NO 物理页号(即行所在的数据页编号)
12 ~ 15 4 SLOT_NO 页内的槽位号(行在该页中的具体位置)

所以我们可以之间通过PHYROWID来获取页号,先以主键为聚簇索引的表TEST_WITH_CLUSTER为例,通过方式一查到的页号是:

image.png

再通过PHYROWID去查

SELECT DISTINCT SUBSTR(CAST(PHYROWID AS VARBINARY), 1, 12) AS PAGE_NO FROM TEST_WITH_CLUSTER ORDER BY PAGE_NO;

image.png

后8字节才是物理页号,跟方式一查到的看似不一样,但是我们把16进制转成10进制4CC0=19648,后面的4CC1对应19649,对应了方式一的页根和后面的页号,也验证了两种方式的可行性。

该方法的优势

1、对于方法一不支持的表类型,通过PHYROWID也能直接查出物理页号

2、灵活性更好,精确到每一行的PAGE_NO信息,没有查询条件限制

3、查询语句更简洁,能直接得出页号

三、应用场景

1、数据页和数据包导出

知道页号后,可以用达梦的dmrman中使用dump page按表空间 ID、文件 ID 和数据页号把指定页的内容 dump 出来,看到页头、槽位、行数据的原始结构。用途:

  • 怀疑页内容异常时做取证,看页里到底存了什么;
  • 坏页前后各 dump 一次做对比,页里哪里出了问题;

表空间ID、文件ID的获取非常简单,直接查询V$DATAFILE即可

image.png

可以指定备份名或者备份集路径的方式dump,比如我想导出DM表,在MAIN表空间,0号文件,其页号为54064:

image.png

image.png

2、用于页级还原(DMRMAN)

当某些页损坏时,不需要还原整个表空间或整库。把文件号+文件 ID+页号交给 DMRMAN,就能只从备份集还原这几个页:

image.png

知道要还原的具体表的页号,就可以在备份还原上大大减少工作量,提高效率。

3、用于DMDBCHK数据库校验

做全库检查开销大。知道核心表的页号范围后,可以把检查重点放在这些页上,优先对单个数据文件进行校验,提高效率。

./dmdbchk PATH=E:\break13\DAMENG\dm.ini PAGES_FILE=E:\break13\DAMENG\pages.txt

pages.txt 格式如下:

#需要校验的所有页号 page(5, 0, 49) page(5, 0, 50) page(5, 0, 51) page(5, 0, 52) page(500, 0, 52) page(5, 0, 30), page(5, 0, 31), page(5, 0, 32)

检测后的报告存在 dmdbchk 工具所在的目录里,名称为 dbchk_err.txt。报告内容如下:

/**一dmdbchk版本信息**/ [2024-12-27 14:28:42]dmdbchk V8 /**二所有指定页的检测结果**/ [2024-12-27 14:28:43]--------check pages file start--------- [2024-12-27 14:28:43][CHK] dbchk check_one_page error. page(5, 0, 49) data check error [2024-12-27 14:28:43][CHK] dbchk check_one_page error. page(5, 0, 50) data check error [2024-12-27 14:28:43][CHK] dbchk check_one_page error. page(5, 0, 51) data check error [2024-12-27 14:28:43][FAILED] fsm_check_page_validate page(500, 0, 52) is invalidate [2024-12-27 14:28:43][FAILED] fsm_check_page_validate page(5, 0, 30) is invalidate [2024-12-27 14:28:43][FAILED] fsm_check_page_validate page(5, 0, 31) is invalidate [2024-12-27 14:28:43]--------check single dbf file end----------- [2024-12-27 14:28:43]DM DB CHECK END...... /**三总数归类**/ [2024-12-27 14:28:43] Checked Files Total: 0 Checked Indexes Total: 0 Error Indexes Total: 0 Checked Pages Total: 8 Corrupted Pages Total: 0 Data Error Pages Total: 3 Invalidate Pages Total: 3 Checked Tablespaces Total: 0 Error List Total: 0 Checked Inodes Total: 0 Error Inodes Total: 0
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服