PG由于系统内核配置不够导致启动报错以及事务ID回卷问题解决方法参考

max_connections VS semget

max_connections是最大连接数,即允许客户端连接的最大连接数,增大连接可以允许接入更多的客户端,但设置过大同样会造成DB启动失败
semget是获取信号的一个函数,即get semaphore

[postgres@localhost ~]$ vi /database/pgdata/postgresql.conf 
max_connections = 5000
[postgres@localhost ~]$ pg_start
server starting
[postgres@localhost ~]$ FATAL:  could not create semaphores: No space left on device
DETAIL:  Failed system call was semget(1949125, 17, 03600).
HINT:  This error does *not* mean that you have run out of disk space.  It occurs when either the system limit for the maximum number of semaphore sets (SEMMNI), or the system wide maximum number of semaphores (SEMMNS), would be exceeded.  You need to raise the respective kernel parameter.  Alternatively, reduce PostgreSQL's consumption of semaphores by reducing its max_connections parameter.
        The PostgreSQL documentation contains more information about configuring your system for PostgreSQL.

上述的空间不够不是指的是磁盘空间不够,而是创建semaphores时空间参数不够,系统调用参数semget报错,但是错误信息感觉有些迷惑…解决办法通常是减小max_connections,或者增大内核参数,如semmni,semmns等,在/proc/sys/kernel/sem里面调整,如

[root@localhost ~]# sysctl -w kernel.sem="500 64000 50 150"
kernel.sem = 500 64000 50 150
[root@localhost ~]# cat /proc/sys/kernel/sem
500     64000   50      150

运维建议

由于参数设置问题或者其他问题,造成freeze 失败,导致数据库最老的表年龄达到了1000万的时候,数据库会打印如下的

HINT:  To avoid a database shutdown, execute a database-wide VACUUM in "mydb".

根据提示,对该数据库执行vacuum free命令,可以解决这个潜在的问题。注意因为非超级用户没有权限更新database的datfrozenxid,只能使用超级用户执行acuum free database_name。如果数据库可用的XID 空间还有100万的时候,即当前最新XID 与数据库最老的XID 的差值还差100万达到20亿,则PostgreSQL 会变为只读并拒绝开启任何新的事务,同时在日志中打印如下错误信息:

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.

如果出现了这种情况,根据提示,用户可以以单用户模式(single-user mode,详见链接)的方法启动PostgreSQL并执行vacuum freeze命令。可以看出,参数的正确设置是非常重要的。但是上文说过即使参数设置的比较合适,因为不能预测freeze 发生的时间,如果freeze发生的时间正好是数据库比较繁忙的时间,这就会造成IO资源争抢,导致正常的业务受损。用户可以自己监控数据库和表的年龄,在业务比较空闲的时间主动执行以下操作:查询当前所有表的年龄,SQL 语句如下:

SELECT c.oid::regclass as table_name,
     greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');

查询所有数据库的年龄,SQL 语句如下:

SELECT datname, age(datfrozenxid) FROM pg_database;

参考:

https://my.oschina.net/Kenyon/blog/120355
http://mysql.taobao.org/monthly/2018/03/08/

参考2

https://blog.csdn.net/Hehuyi_In/article/details/102869893
这篇文章可以看看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值