在PG中事务年龄不能超过2^31 (2的31次方=2,147,483,648),如果超过了,这条数据就会丢失。
PG中不允许这种情况出现,当事务的年龄离2^31还有1千万的时候,数据库的日志中就会
有如下告警:
warning:database “UniMonDB” must be vacuumed within 177000234 trabnsactions
HINT: To avoid a database shutdown,execute a database-wide VACUUM in “UniMonDB”.
如果不处理,当事务的年龄离2^31还有1百万时,数据库服务器出于安全考虑,将会自动
禁止任何来自任何用户的连接,同时在日志中是如下信息:(某个现场服务器的pg日志截图)
然后我们去查看这个表的事务id
也是因为某个表的事务id用完了,出现的这个问题,我们的程序使用了otlv4的相关数据库接口,会导致程序操作数据库的时候崩溃。所以不管怎样都需要解决事务id用完这个问题。看过其他方案,进入pg的单用户模式对全表进行vacuum freeze或者vacuum full。我们根据自身情况做对应处理,以下是实际生产环境上的操作步骤。
对于事务id消耗较少的表:
先回收空间
1)、停止数据库服务
2)、备份数据库,确定数据库停止后,可以直接copy一份main目录
tar -cvzf /var/lib/postgresql/9.1/main_back.tar /var/lib/postgresql/9.1/main
3)、备份好后,启动数据库
4)、查询死元组数据的十张表,并将表名记录下来
select relname, coalesce(round(n_dead_tup * 100 / (case when n_live_tup + n_dead_tup = 0 then null else n_live_tup+n_dead_tup end), 2),0.00) as dead_tup_ratio, n_dead_tup ,n_live_tup, last_vacuum, last_autovacuum from pg_stat_all_tables where relname like ‘tbl_%’ order by n_dead_tup desc limit 10;
5)、一张表一张表清理,清理命令(vacuum full 表名)
6)、vacuum执行完后,再查看死元组数量,表名用第四步查出来的表名
select relname, coalesce(round(n_dead_tup * 100 / (case when n_live_tup + n_dead_tup = 0 then null else n_live_tup+n_dead_tup end), 2),0.00) as dead_tup_ratio, n_dead_tup ,n_live_tup, last_vacuum, last_autovacuum from pg_stat_all_tables where relname in (‘tbl_formconfig’,‘tbl_userpermission’,‘tbl_iniext’,'tbl_processlistinfo ',‘tbl_userloginerror’,‘tbl_devchangeinfo’,‘tbl_secpolicysetinfo’,‘tbl_devbaseinfo’,‘tbl_pcidevinfo’,‘tbl_ini’);
7)、这是因为pg_stat_all_tables的数据没有几十更新,也就是第四步查询的数据不会变,如下
8)、这时候需要手动更新相关表数据才行,表名用第四步查出来的表名
analyze tbl_formconfig, tbl_userpermission, tbl_iniext, tbl_processlistinfo , tbl_userloginerror, tbl_devchangeinfo, tbl_secpolicysetinfo, tbl_devbaseinfo, tbl_pcidevinfo, tbl_ini;
然后再用第6步的命令查询就ok了。
而后再回收事务id
1)、停止数据库服务
2)、备份数据库,确定数据库停止后,可以直接copy一份main目录
3)、tar -cvzf /var/lib/postgresql/9.1/main_back.tar /var/lib/postgresql/9.1/main
4)、备份好后,启动数据库
5)、查出表年龄最大的十张表,记住表名
select relname,age(relfrozenxid) from pg_class where relkind in (‘t’,‘r’) and relname like ‘tbl_%’ order by age(relfrozenxid) desc limit 10;
6)、处理表事务id ,第五步记住的表名
vacuum freeze 表名;
7)、查询处理后的状态,第五步记住的表名
select relname,age(relfrozenxid) from pg_class where relkind in (‘t’,‘r’) and relname in (‘tbl_acaccountinfo’, ‘tbl_acaccountmgm’, 'tbl_acaccountmgmresult ', 'tbl_acagentaccessaudit ', ‘tbl_acagentinfo’, ‘tbl_acauditinfo’, ‘tbl_acauditsecpolicy’, ‘tbl_acbaseparam’, ‘tbl_acl’, ‘tbl_aclentry’);
而如果事务id的使用情况是tbl_devprofile这种情况。vacuum可能会非常的耗时,并且可能会失败并且出现一系列的其他问题,增加处理的负担。
对此不妨尝试重建新库的方式来重置事务id
pg迁移旧数据库内容到新库
注!!/var/lib/pgsql/是linux用户postgres的家目录,不同环境可能会不通,请自己修改nohup pg_dumpall -U postgres -h 127.0.0.1 -p 5432 -f /root/backup.sql & --先导出原UniMonDB的所有数据
chmod 777 /root/backup.sql ; chown leagsoft:leagsoft /root/backup.sql --修正权限
mv /root/backup.sql /var/lib/pgsql/ --把备份数据放到postgres的家目录下
然后
su - postgres --切换至postgres
/usr/pgsql-11/bin/initdb -d ~/main --初始化一个全新的库,面子也叫main,暂时放在postgres的家目录下
cp /var/lib/postgresql/9.1/main/postgresql.conf ~/
cp /var/lib/postgresql/9.1/main/postgresql.auto.conf ~/
cp /var/lib/postgresql/9.1/main/pg_hba.conf ~/ --将旧库的是哪个配置文件先先放到postgres的家目录chmod 600 postgresql.conf postgresql.auto.conf pg_hba.conf --修正权限
cp postgresql.conf postgresql.auto.conf pg_hba.conf ~/main --替换新库的配置文件vi ~/main/postgresql.conf --把里面的端口从5432改成别的,比如6432
/usr/pgsql-11/bin/pg_ctl -D main start --手动启动的方式启动新库
psql -p 6432 --进入新库控制台
\i backup.sql --导入备份的数据到新库
\q --退出控制台
vi ~/main/postgresql.conf --把里面的端口改回5432切回root
systemctl stop postgresql --软件将/var/lib/postgresql/9.1/main的启动注册称为了系统服务,停止旧库
mv /var/lib/postgresql/9.1/main /var/lib/postgresql/9.1/main_old --将旧库备份
mv /var/lib/pgsql/main /var/lib/postgresql/9.1/main --将新库放到系统服务的指定路径
systemctl start postgresql --启动新库一切恢复正常
所以实际生产中可以定期去做收缩回收操作
以上是从同事那偷学的解决方案