暂无图片
暂无图片
5
暂无图片
暂无图片
1
暂无图片

复现HINT: To avoid a database shutdown,execute a database-wide vacuum in that database

原创 云龙 2020-09-06
4115

背景

PostgreSQL中xid(事务id)的数量是有限制的(32bit,约42.9亿个),其中有3个保留xid(0:无效的xid;1:初始化时创建catalog的xid;2:冻结xid),为了防止xid数量不足引起xid回卷的情况,使用冻结xid(FrozenTransactionId)来保证所有历史事务都比普通事务都老,数据被普通事务可见,且有足够的空闲xid供将来的事务使用。

需要定期执行vacuum操作或设置数据库参数来触发autovacuum进程将符合条件的事务xmin值更新为2(不同版本实现方式不一样)。即使关闭autovacuum参数且从未执行过vacuum命令,当事务的age达到了autovacuum_freeze_max_age参数值时,数据库依然会强制执行vacuum freeze命令。如果强制执行vacuum 失败,则有xid用尽的风险,当数据库的最旧 XID 和回卷点之间剩余1千万个事务时,系统将开始发出这样的警告消息

WARNING:  database "mydb" must be vacuumed within 177009986 transactions
HINT:  To avoid a database shutdown, execute a database-wide VACUUM in "mydb".
复制

距离回卷点只剩下1百万个事务时,该系统将会关闭并且拒绝开始任何新的事务

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.
复制

这时只能关闭数据库,然后以单用户模式进入相应的数据库执行vacuum操作

/bin/postgres --single -D /data/pgdata mydb
backend> vacuum;
复制

经历

维护PostgreSQL数据库这么久,曾经遇到过一次这样的报警提示,原因是索引文件磁盘坏块,导致数据库无法有效的进行vacuum操作,并且这个索引也无法执行reindex命令进行重建,只好把这个索引删除重建创建,数据库可以正常进行autovacuum操作,问题得到解决。

重现原因

华为公司开源了opengauss数据库,而这个数据库是基于PostgreSQL 9.2.4的内核版本进行封装开发的,opengauss数据库改进的一个特性是将postgresql的xid限制由32位提升到了64位,这也就意味着opengauss的xid永远用不完,也就不会涉及到事务回卷的情况发生,为了验证这一点,需要PostgreSQL 和 opengauss 一起来验证。

重现步骤

问题重现有一个难点就是当age达到autovacuum_freeze_max_age参数值后如何有效的阻止数据库强制执行vacuum freeze操作。

事务id不能像序列那样指定初始值,只能以interval=1的方式自增,所以没有捷径可以走,只能刷事务id。

数据库安装

这里使用的是PostgreSQL 9.2.4 源码安装,安装步骤略

参数修改

autovacuum_freeze_max_age = 2000000000	# maximum XID age before forced vacuum
vacuum_freeze_min_age = 1000000000
vacuum_freeze_table_age = 2000000000
复制

重现

有效阻止数据库出发vacuum freeze的方式有两种:
1、新增一个表,通过begin命令开启一个事务,执行一个ddl 或 dml 操作,让表或表中的一行数据持有排他锁,并且事务不提交不回滚,无法让vacuum获取锁,这时新开一个会话开始刷事务id

2、新增一个表,插入几条数据,然后直接在系统里删除数据文件(和之前遇到的索引问题类似),找不到数据文件,无法执行vacuum操作,这时新开一个会话开始刷事务id

由于我自己机器的配置太低,用第一种方法不能准确的控制会话连接,所以我选择用第二种方式

建表

新建两张表,各插入一条数据,查看当前数据的事务id,当前数据所在表的冻结id以及当前表所在数据库的冻结id

create table vacuum_test(id int);
create table vacuum_test_1(id int);

insert into vacuum_test select 1;
insert into vacuum_test_1 select 1;

select xmin,age(xmin),* from vacuum_test;

select relname,relfilenode,relfrozenxid from pg_class where relname like 'vacuum_test%';

select oid,datname,datfrozenxid from pg_database where datname='mydb';
复制
重命名数据文件

通过上面的命令知道了表所对应的物理文件名(relfilenode),在base/数据库oid/relfilenode
直接用mv 命令替换就好,比如

mydb=# select xmin,age(xmin),* from vacuum_test;
   xmin    | age | id
-----------+-----+----
 636973319 |   1 |  1
(1 row)

mydb=# select relname,relfilenode,relfrozenxid from pg_class where relname like 'vacuum_test%';
   relname   | relfilenode | relfrozenxid
-------------+-------------+--------------
 vacuum_test |       24605 |    636973318
(1 row)

mydb=# select oid,datname,datfrozenxid from pg_database where datname='mydb';
  oid  | datname | datfrozenxid
-------+---------+--------------
 24604 | mydb    |    636973312
(1 row)


$ ll base/24604/24605
-rw------- 1 postgres postgres 8192 Sep  6 00:19 base/24604/24605

$ mv base/24604/24605 base/24604/24605_old
$ ll base/24604/24605*
-rw------- 1 postgres postgres 8192 Sep  6 00:24 base/24604/24605_old
复制
pgbench 批量执行

人工刷txid太慢了,用压测工具来跑,省时省力
pgbench 工具安装很简单
在postgresql的源码文件contrib/pgbench下执行 make && make install

cat pgbench.sql
BEGIN;
select txid_current();
end;

--执行
pgbench -c 60 -j 60 -n -f pgbench.sql -T 36000 mydb &
开启的客户端数量和线程数量根据服务器硬件配置改动
复制
对比测试

定时执行一次下面的命令,跟踪一下

insert into vacuum_test_1 select 1;
select xmin,age(xmin),* from vacuum_test_1;
select relname,relfilenode,relfrozenxid from pg_class where relname like 'vacuum_test%';
select oid,datname,datfrozenxid from pg_database;
复制

然后就是慢慢的等待。。。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

11 0
暂无图片
1年前
评论
暂无图片 0
👍
1年前
暂无图片 点赞
评论