为何asyncpg连接池比单连接运行速度慢2倍?
问题分析:连接池版本比单连接慢一倍的原因
你的测试结果里连接池版本更慢,主要是实现细节和测试场景的问题,而非数据库端的问题,具体原因如下:
1. 每次查询重复执行连接获取/释放的额外开销
单连接版本复用同一个连接完成所有100次查询,没有额外的连接调度开销;而连接池版本的fetch_all方法每次都执行:
async with pool.acquire() as conn:
这意味着每次查询都要从连接池中申请连接、用完后归还,这个过程包含连接池内部的锁竞争、连接状态校验等操作,串行执行100次的话,这些额外步骤的开销会被累积放大。
2. 查询结果的格式转换开销
连接池版本的fetch_all把查询结果从asyncpg.Record强制转成了dict:
return [dict(row) for row in await conn.fetch(query)]
而单连接版本直接返回asyncpg.Record对象。dict(row)的转换是纯CPU开销,如果user表字段较多、结果集较大,100次转换的累积开销非常可观。
3. 测试场景未匹配连接池的优势
连接池的核心价值是高并发场景下复用连接、避免频繁创建销毁连接的开销,而你的测试是串行执行100次查询:单连接串行本来就是最优模式(没有连接创建/切换开销),连接池在这种场景下反而会因为额外的调度逻辑拖慢速度。
改进建议
1. 调整测试逻辑做公平对比
如果要公平对比,连接池版本也应该复用单个连接执行所有查询,和单连接的逻辑对齐:
pg_conn = PostgresConnection() pool = await pg_conn.get_pool() async with pool.acquire() as conn: for i in range(100): rows = await conn.fetch('SELECT * FROM "user"') # 按需转换为dict
这样就能消除每次获取/释放连接的开销,测试结果会和单连接版本接近。
2. 去掉不必要的结果转换
如果业务不需要dict格式,直接返回asyncpg.Record即可,避免转换开销:
async def fetch_all(self, query: str) -> list[asyncpg.Record | None]: pool = await self.get_pool() async with pool.acquire() as conn: return await conn.fetch(query)
3. 用并发场景测试连接池的真实优势
连接池的优势在并发场景才能体现,比如用asyncio.gather同时执行多个查询:
import asyncio pg_conn = PostgresConnection() tasks = [pg_conn.fetch_all('SELECT * FROM "user"') for _ in range(100)] await asyncio.gather(*tasks)
这种场景下,连接池可以同时复用多个连接处理请求,总耗时会远低于单连接串行的5秒。
内容的提问来源于stack exchange,提问作者Maksim Burtsev
相关产品推荐
相关产品推荐

