一、背景介绍
PostgreSQL的最大连接数在某些时候会成为一个让业务很苦恼的问题。业务连接数暴增,连接数打满,但又不能重启服务这种场景下,显得非常尴尬。最大连接数就只能重启调整,要想不停库,貌似“无解”。
为什么调整最大连接数需要重启,不能修改为reload方式?
这个问题不详细展开,简单来说最大连接数参数和共享内存及信号量等相关的,设置最大连接数后,在启动时系统会相应分配这些资源。那原生的最大连接数参数看起来只能是重启方式修改生效了。
原生pg除了max_connections,还可以ALTER USER / DATABASE … CONNECTION LIMIT connlimit通过user和 database这两个维度去动态调整连接数。不过只适用于比较特定的场景下,稍微推敲一番,就会发现大部分场景下不适用。比如同实例多database,多user,并且每个user需要一定数量的连接数,那瓶颈又回到了max_connections
思考了一番,决定尝试新增一个在线参数db_connlimit,可以动态调整最大连接数,参数级别为PGC_SIGHUP即reload生效。
默认值为-1,保持默认值,最大连接数还是沿用原生的逻辑。取值范围从-1(default)至max_connnections 。也就是说系统资源还是由max_connections参数来管理,db_connlimit参数只用来管理连接数个数,资源满足情况下适当放大max_connections,例如将db_connlimit设置为max_connections的一半,在连接数已满的情况下,可以继续增大db_connlimit,来缓解一些压力。
当然这个方案是存在问题的:例如未考虑版本差异。要想做到兼容所有版本,那么可以考虑HooK extensions 或者中间件方式,不过这样架构可能会更加复杂了。研发大佬肯定有成熟的方案,作为运维同学,我们就最后挣扎一下,验证下这个方案。
这里使用pg13.0进行测试。
二、问题思考
db_connlimit参数必须满足以下几点要求:
- 对原生影响要小,可以关闭(default为关闭)关闭后不影响原生逻辑;
- 可靠性,连接数达到最大时要和原生处理逻辑一致;
- 最大值生效问题,最大值必须小于等于max_connnections 当试图配置大于这个值时reload不能生效,并且保持原有值
三、代码修改
针对以上3个问题,依次来看:
- 增加参数db_connlimit
global.c 设置全局变量DbConnlimit
/*
* Modify by Nickxyang at 2021-06-07 PM
* Add the global variable DbConnlimit.
*/
int DbConnlimit = 100;
/*
* Modify End at 2021-06-07 PM
*/
miscadmin.h
/*
* Modify by Nickxyang at 2021-06-07 PM
*/
extern PGDLLIMPORT int DbConnlimit;
/*
* Modify End at 2021-06-07 PM
*/
guc.c新增参数db_connlimit, 首个-1为default,第二个-1为最小值
/*
* Modify by Nickxyang at 2021-06-07 PM
* Add the parameter db_connlimit, you can modify the maximum number of connections online.
* The valid range is -1 (default) to Maxconntions. A value of -1 means that the parameter is not open.
*/
{
{
'db_connlimit', PGC_SIGHUP, CONN_AUTH_SETTINGS,
gettext_noop('Sets the maximum number of concurrent connections, which can be set online.'),
NULL
},
&DbConnlimit,
-1, -1, MAX_BACKENDS,
check_dbconnlimit, NULL, NULL
},
/*
* Modify End at 2021-06-07 PM
*/
guc.c增加check函数,该函数由函数指针指向,在initdb以及start环节check该参数合法性
/*
* Modify by Nickxyang at 2021-06-07 PM
*/
static bool
check_dbconnlimit(int *newval, void **extra, GucSource source)
{
if ((*newval + autovacuum_max_workers + 1 +
max_worker_processes + max_wal_senders > MAX_BACKENDS) && *newval > MaxConnections)
return false;
return true;
}
/*
* Modify End at 2021-06-07 PM
*/
postgresql.conf.sample
#db_connlimit = -1 # range -1 to max_connections
- 保证对原生影响不大,参数关闭时,不影响原生逻辑
- 可靠性,保证达到最大连接数时,处理逻辑和原生一致
postinit.c 中InitPostgres函数
/*
* Modify by Nickxyang at 2021-06-07 PM
*
* If the DbConnlimit parameter is configured
* and the number of connections reaches DbConnlimit - ReservedBackends,
* non-superuser will not be able to create new connections
*/
if (DbConnlimit > 0 && DbConnlimit < MaxConnections)
{
if (!am_superuser && !am_walsender
&& ReservedBackends > 0 &&
!HaveNFreeProcs(MaxConnections - DbConnlimit + ReservedBackends))
ereport(FATAL,
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg('remaining connection slots are reserved for n