Npgsql连接池Minimum Pool Size未生效问题排查
问题描述
在多Pod部署的.NET应用中,我使用以下PostgreSQL连接字符串:
"User ID=;Password=;Host=;Port=;Database=;Pooling=true;Username=;ApplicationName=;Max Auto Prepare=200;Minimum Pool Size=90;Maximum Pool Size=100;Read Buffer Size=18000;Timeout=1;Command Timeout=5;"
(注:我知道Minimum Pool Size=90设置过高,这只是为了排查问题)
我预期每个Pod与数据库服务器的活跃连接数在90到100之间,但执行以下查询:
select client_addr, count(*) used FROM pg_stat_activity where datname = 'my_app_name' group by client_addr;
结果显示每个Pod最多只有约30个连接。
我的C#代码中连接使用及查询执行方式如下:
public async Task<IEnumerable<T>> GetGeneric<T>(string query, DynamicParameters parameters) { await using var connection = DbConnectionHelper.Create(myConnectionString); return await connection.QueryAsync<T>(query, parameters); }
DbConnectionHelper实现:
public static class DbConnectionHelper { public static NpgsqlConnection Create(string connectionString) { var connection = new NpgsqlConnection(connectionString); return connection; } }
根据ADO.NET连接池文档说明:
当连接池创建时,会创建多个连接对象并添加到池中,以满足最小池大小要求。
这意味着每个Pod在应用启动时应创建90个连接,不是吗?
由此产生以下疑问:
- 为什么
Minimum Pool Size未生效? - 为什么执行
select client_addr, count(*) used FROM pg_stat_activity看不到90到100之间的数值?
问题解答
1. 为什么Minimum Pool Size未生效?
首先要明确:连接池的最小连接数是池初始化时创建的空闲连接数,但这些连接不会自动处于"活跃"状态。Npgsql(以及ADO.NET连接池)的核心逻辑是:
- 连接池是延迟初始化的:只有当应用第一次创建并打开连接时,池才会被创建,此时才会初始化指定数量的空闲连接,而非应用启动时就直接创建。
- 池中的空闲连接只是保存在应用本地,并未与数据库建立活跃会话——只有当应用调用
Open()/OpenAsync()(或Dapper内部自动触发)时,才会将空闲连接转为活跃状态,用于执行查询。
你的代码中,每次查询完成后await using会自动释放连接,将其放回池转为空闲状态。没有查询请求时,池里的连接都是空闲的,不会主动保持活跃连接到数据库,自然不会达到你预期的90个活跃连接数量。
2. 为什么pg_stat_activity看不到90-100的连接数?
pg_stat_activity仅显示当前处于活跃状态的数据库会话(正在执行查询、或处于打开但未释放状态的连接)。而你的代码逻辑带来两个关键影响:
- 每次查询完成后连接立即被释放回池,转为空闲状态,这些空闲连接不会出现在
pg_stat_activity的活跃统计中(服务器端会标记为idle,如果配置了空闲超时,超时后会被销毁)。 - 实际活跃连接数由应用的并发请求量决定:只有当Pod同时有90个以上的并发查询时,才会用到90个活跃连接。如果你的Pod并发请求峰值仅为30左右,自然只会看到对应数量的活跃连接。
补充:连接池的最小连接数作用是避免高并发时频繁创建销毁连接的开销,保证池内始终有指定数量的空闲连接待命,但这些空闲连接不会强制保持与数据库的活跃会话。
内容的提问来源于stack exchange,提问作者Maciej Pszczolinski
相关产品推荐
相关产品推荐

