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
这篇文章可以看看