1.背景
数据库目录下有多种二进制文件,比如系统表、普通表、索引和日志文件等等,但是数据库运行过程中的问题,我们该如何利用这些文件去定位和分析问题呢?利用Pagehack工具帮助我们在故障定位中,解析各种文件。
功能特点:
- 可视化数据页:PageHack提供了一个可视化的界面,允许用户直接查看数据库中的数据页。这有助于了解数据的物理存储方式,例如行如何存储在页中,以及页中哪些空间被有效利用,哪些空间被浪费。
- 修改数据页:除了查看数据页,PageHack还允许用户直接修改数据页。这可以用于修复损坏的数据,或者在不通过常规SQL语句的情况下进行数据操作。
- 性能调优:通过分析数据页,你可以发现性能瓶颈,例如页碎片过多或数据分布不均。通过调整数据页的布局,可以显著提高数据库的性能。
2.介绍
2.1 Pagehack --help 用法参数介绍
2.2 部分解析方法
(1)数据库中的系统表有很多,可以通过pagehack查询data目录下的pg_filenode.map将系统表和磁盘上的文件一一对应。
pagehack -f pg_filenode.map -t filenode_map
这里的relfilenode就对应磁盘上的文件。
(2) 对xheap undo的解析
Undo 目录:
初始化集群
解析命令:
pagehack -f undometa -t undo_zone
pagehack -f undometa -t undo_space -z zid
pagehack -f permanent/00000.meta.0000000 -t undo_slot
pagehack -f permanent/ -t undo_record -o urp
2.3 回滚段
旧版本数据会集中在回滚段的undo目录中,为了减少读写冲突,旧版本数据(回滚记录)采用追加写的方式写入数据目录的undo目录下。这样旧版本数据的读取和写入不会发生冲突,同一个事务的旧版本数据也会连续存放,便于进行回滚操作。为了减少并发写入时的竞争,undo目录空间被划分成多个逻辑区域(UndoZone,回滚段逻辑区域)。在分配undo空间时会按照事务粒度进行记录,旧版本数据一旦确认没有事务进行访问,就会进行回收。
为了在回滚段的空间寻址,回滚记录使用8字节的指针来进行寻址,如图所示。
其中各个字段的含义如下:
(1) zoneId:占用20bit,表示逻辑区域的ID。
(2) blockId:占用31bit,表示块号,默认为8k。
(3) offset:占用13bit,表示块内偏移。
各个字段的含义如下。
(1) xid:生成此回滚记录的事务ID,用于检查事务的可见性。
(2) CID(Command ID,命令ID):生成此回滚记录的命令ID,用于判断可见性。
(3) reloid:relation对象的ID,回滚时需要。
(4) relfilenode:relfilenode对象的ID,回滚时需要。
(5) utype:操作类型,像UNDO_INSERT、UNDO_DELETE、UNDO_UPDATE等。
(6) uinfo:控制字段,用来判断后续的结构是否存在,用来减少回滚记录的占用空间。
(1) blkprev:指向同一个block前一条回滚记录,用于回滚和事务可见性。
(2) blkno:block number(块号)。
(3) Offset:修改的tuple在row pointer中的偏移。
wtxn_成员由下面的结构组成。
typedef struct {
UndoRecPtr prevurp;
} UndoRecordTransaction;
prevurp:当一个事务的回滚记录跨越两个UndoZone时,后续的回滚记录使用此指针指向前一条回滚记录。
wpay_成员由下面的结构组成。
typedef struct {
UndoRecordSize payloadlen;
}
payloadlen:rawdata_的长度。
wtd_成员由下面的结构组成。
typedef struct {
TransactionId oldxactid;
} UndoRecordOldTd;
oldxactid:旧版本数据里事务目录的事务ID。
wpart_成员由下面的结构组成。
typedef struct {
Oid partitionoid;
} UndoRecordPartition;
partitionoid:分区表的分区对象OID。
wtspc_成员由下面的结构组成。
typedef struct {
Oid tablespace;
} UndoRecordTablespace;
tablespace:表空间的OID。
3.适配情况
以下对解析undo_zone、undo_record的解析逻辑进行说明。
解析undo_zone逻辑:
-
解析命令参数
-
读取undometa文件
-
设置读取的起始位置
-
根据偏移量设置每个undozonemeta信息
-
打印信息
解析undo_record逻辑:
-
解析命令参数
-
根据urp参数解析 zoneid、blockno、startingbyte
-
根据zoneid、blockno去打开对应的undo记录文件
-
设置读取的起始位置
-
根据undo_record记录的结构体大小逐项复制,其中whdr_结构体内有 uinfo属性标识用来判断后续的结构是否存在,有则读取,没有就跳过。
-
打印信息
解析undo_record前先需要建表插数据产生undo记录文件和事务文件。
CREATE TABLE xt ( c1 int not null, c2 int not null, c3 int not null ) WITH ("storage_type"="xstore");
insert into xt values (1,1,1),(2,2,2),(3,3,3),(4,4,4);
undo/permanent 下产生了undo记录文件和undo事务文件。
undo记录文件格式: (zoneid).(segno) ,即前面部分00000 对应 zoneid=0.后面部分0000000对应segno=0. 每个seg空间为1M。
undo事务文件格式: (zoneid).meta.(segno) ,每个seg空间为32K。
解析undo_record命令需要urp参数。
查找urp方法:
-
利用pg_undo_translot_dump_slot函数(查看某个undo zone 对应undoslot的明细列表)
-
查看打印日志 (集群需设置log_min_messages = 'debug5')
grep write.*undorecord log/postgresql-...-.csv
undo_record解析结果
pagehack -f permanent/ -t undo_record -o 36
注意-o urp参数为十进制