V$SEGMENTINFO 性能陷阱)在日常运维中,我们常需获取一张表的实际数据页号(PAGE_NO)、段号、簇号等信息,用于热点分析、存储分布排查等操作。借助动态视图 V$SEGMENTINFO通常能获取完全段号、簇号、数据页号等信息,但该视图存在严重的性能隐患——每次查询都会全量扫描所有数据文件,对于生产大库而言基本不可用,同时还有条件限制,本文将指出这一弊端,并给出一个稳定、简洁的替代方案。
达梦的表(列存储表和堆表除外)都是索引组织表,而通常都有一个聚集索引,当你创建一个表,并且没有显式指定聚集索引键时,达梦数据库会自动选择ROWID作为聚集索引键。
对于索引组织表,数据是存在LEAF_SEG叶子节点段上的,所以可以根据v$segmentinfo获取对应聚集索引信息
通过系统视图dba_indexes和dba_objects,根据表名和模式名定位聚集索引。聚集索引的INDEX_TYPE为CLUSTER,且索引名通常以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%';
查询动态视图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%'
);
有了段号,查对应的簇号也是很简单的:
1、该方法只适用于普通表(索引组织表),对于堆表和列存储表不适用。
例如创建一个堆表,去查询的时候就卡在第一步了,查不到OBJECT_ID
2、由于v$segmentinfo这个视图每次查的时候,都要全扫数据文件,如果全部数据文件扫一遍,对大库来说基本就是不可用的。
3、灵活性不够高,无法具体到某一条数据的物理页号所在,并且查询视图时,只能用等值条件,无法用范围查询WHERE seg_id > 100。
伪列 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为例,通过方式一查到的页号是:
再通过PHYROWID去查
SELECT DISTINCT
SUBSTR(CAST(PHYROWID AS VARBINARY), 1, 12) AS PAGE_NO
FROM TEST_WITH_CLUSTER
ORDER BY PAGE_NO;
后8字节才是物理页号,跟方式一查到的看似不一样,但是我们把16进制转成10进制4CC0=19648,后面的4CC1对应19649,对应了方式一的页根和后面的页号,也验证了两种方式的可行性。
1、对于方法一不支持的表类型,通过PHYROWID也能直接查出物理页号
2、灵活性更好,精确到每一行的PAGE_NO信息,没有查询条件限制
3、查询语句更简洁,能直接得出页号
知道页号后,可以用达梦的dmrman中使用dump page按表空间 ID、文件 ID 和数据页号把指定页的内容 dump 出来,看到页头、槽位、行数据的原始结构。用途:
表空间ID、文件ID的获取非常简单,直接查询V$DATAFILE即可
可以指定备份名或者备份集路径的方式dump,比如我想导出DM表,在MAIN表空间,0号文件,其页号为54064:
当某些页损坏时,不需要还原整个表空间或整库。把文件号+文件 ID+页号交给 DMRMAN,就能只从备份集还原这几个页:
知道要还原的具体表的页号,就可以在备份还原上大大减少工作量,提高效率。
做全库检查开销大。知道核心表的页号范围后,可以把检查重点放在这些页上,优先对单个数据文件进行校验,提高效率。
./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
文章
阅读量
获赞
