pg_resetwal -O multixact-offset
本期播客
奇怪的知识: pg_resetwal -O multixact-offset 算法解释
某些情况下, 例如数据库异常停库并且少了需要用于恢复数据库的WAL、控制文件丢失等情况, 可能需要使用pg_resetwal重置控制文件信息, 让数据库临时能起来.
要设置一些值, 例如 multixact-offset , 能从数据文件的相关目录中获取安全值, 所谓安全值, 就是防止新设置的值的起点比实际值更早. 因为重置后multixact的状态ID将从重置值开始继续向前写, 如果小于已有值, 可能复写已有文件的multixact状态标记, 导致已发生的multixact状态异常.
那么到底怎么取值? 我们可从代码中了解. 以pg_resetwal -O 选项中 multixact-offset 的值为例.
$ pg_resetwal --help
pg_resetwal resets the PostgreSQL write-ahead log.
Usage:
pg_resetwal [OPTION]... DATADIR
Options:
[-D, --pgdata=]DATADIR data directory
-f, --force force update to be done even after unclean shutdown or
if pg_control values had to be guessed
-n, --dry-run no update, just show what would be done
-V, --version output version information, thenexit
-?, --help show this help, thenexit
Options to override control file values:
-c, --commit-timestamp-ids=XID,XID
set oldest and newest transactions bearing
commit timestamp (zero means no change)
-e, --epoch=XIDEPOCH set next transaction ID epoch
-l, --next-wal-file=WALFILE set minimum starting location for new WAL
-m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID
-o, --next-oid=OID set next OID
-O, --multixact-offset=OFFSET set next multitransaction offset
-u, --oldest-transaction-id=XID set oldest transaction ID
-x, --next-transaction-id=XID set next transaction ID
--wal-segsize=SIZE size of WAL segments, in megabytes
Report bugs to <[email protected]>.
PostgreSQL home page: <https://www.postgresql.org/>
如何通过 multixact 目录文件进行计算 pg_resetwal -O 选项中 multixact-offset
multixact-offset 的位数
multixact-offset 是一个 32 位的值。 在代码中定义为 MaxMultiXactOffset ((MultiXactOffset) 0xFFFFFFFF),这表明它是一个 32 位无符号整数。
File: src/include/access/multixact.h (L31-31)
#define MaxMultiXactOffset ((MultiXactOffset) 0xFFFFFFFF)
通过 multixact 目录文件计算其值
根据文档和测试代码,计算 multixact-offset 的方法如下:
计算公式
从 pg_multixact/members 目录中找到数值最大的文件名,加 1,然后乘以 52352 (0xCC80)。
File: doc/src/sgml/ref/pg_resetwal.sgml (L306-311)
A safe value can be determined by looking for the numerically largest
file name in the directory <filename>pg_multixact/members</filename> under the
data directory, adding one, and then multiplying by 52352 (0xCC80).
The file names are in hexadecimal. There is no simple recipe such as
the ones for other options of appending zeroes.
</para>
具体步骤
查看 pg_multixact/members目录下的文件找到十六进制文件名中数值最大的文件 将该文件名转换为十进制并加 1 乘以 52352
代码实现示例
在测试代码中可以看到具体的计算实现:
File: src/bin/pg_resetwal/t/001_basic.pl (L223-225)
@files = get_slru_files('pg_multixact/members');
$mult = 32 * int($blcksz / 20) * 4;
push @cmd, '--multixact-offset' => (hex($files[-1]) + 1) * $mult;
其中 $mult 的计算公式 32 * int($blcksz / 20) * 4 对应文档中提到的 52352 值(当 BLCKSZ = 8192 时)。
技术背景
这个计算基于 PostgreSQL 中 multixact members 的存储结构。每个 member group 包含 4 个字节的标志位和对应的 4 个 TransactionId,总共 20 字节为一组,这解释了公式中除以 20 的原因。
File: src/backend/access/transam/multixact.c (L130-142)
/*
* The situation for members is a bit more complex: we store one byte of
* additional flag bits for each TransactionId. To do this without getting
* into alignment issues, we store four bytes of flags, and then the
* corresponding 4 Xids. Each such 5-word (20-byte) set we call a "group", and
* are stored as a whole in pages. Thus, with 8kB BLCKSZ, we keep 409 groups
* per page. This wastes 12 bytes per page, but that's OK -- simplicity (and
* performance) trumps space efficiency here.
*
* Note that the "offset" macros work with byte offset, not array indexes, so
* arithmetic must be done using "char *" pointers.
*/
/* We need eight bits per xact, so one xact fits in a byte */
注意
multixact-offset 用于跟踪 multixact members 文件中的位置偏移量,与 multixact ID 本身是分开管理的。这个 32 位的限制意味着系统需要定期清理旧的 multixact 数据以防止溢出。
得到的OFFSET要转换为什么进制?
根据文档和代码,计算得到的 multixact-offset 值可以用十进制或十六进制两种进制表示。
支持的进制格式
在 pg_resetwal 的文档中明确说明,对于接受数值参数的选项,可以使用十六进制值,只需要加上 0x 前缀:
File: doc/src/sgml/ref/pg_resetwal.sgml (L168-170)
described below. For values that take numeric arguments, hexadecimal
values can be specified by using the prefix <literal>0x</literal>. Note
that these instructions only apply with the standard block size of 8 kB.
代码实现
在 pg_resetwal.c 的参数解析代码中,使用 strtoul(optarg, &endptr, 0) 来解析 -O 选项的值:
File: src/bin/pg_resetwal/pg_resetwal.c (L268-269)
errno = 0;
set_mxoff = strtoul(optarg, &endptr, 0);
这里的第三个参数 0 表示自动检测进制:
如果输入以 0x开头,会被解析为十六进制否则会被解析为十进制
实际使用示例
在测试代码中,计算出的值直接以十进制形式传递给 --multixact-offset 选项:
File: src/bin/pg_resetwal/t/001_basic.pl (L225-225)
push @cmd, '--multixact-offset' => (hex($files[-1]) + 1) * $mult;
注意
因此,您可以根据需要选择使用十进制(如 52352)或十六进制(如 0xCC80)格式来指定 multixact-offset 值。两种格式都是有效的,pg_resetwal 会自动识别并正确解析。
参考
https://deepwiki.com/postgres/postgres
https://www.postgresql.org/docs/current/app-pgresetwal.html