高并发请求下PG Promise出现too many client错误如何解决?
解决方案
你遇到的too many clients错误核心是连接使用方式错误导致连接被不必要的占用耗尽,不需要调整数据库最大连接数,按以下步骤修改即可解决:
问题根因
- 事务内调用全局
db对象的方法而非事务上下文对象:db.tx回调中传入的t是当前事务绑定的专属连接,你直接用db.none执行插入会每次都从连接池申请新连接,单事务插入上千条数据会直接占满所有连接。 - 异步逻辑无并发控制:
call函数中的循环没有加await管控,5次数据查询+事务逻辑会同时触发,进一步放大连接占用量。 - 批量插入实现方式不合理:逐行生成单条
INSERT语句执行,本身会造成不必要的资源浪费,也会放大连接占用问题。
具体修改方案
1. 修正事务内的API调用
事务内所有数据库操作都使用事务上下文t对象的方法,不要直接调用全局db对象的方法,确保整个事务全程只用1个连接。
2. 改用批量插入语法,一次请求完成所有插入
使用pg-promise内置的helpers.insert工具生成多行插入的单条SQL,只需要执行1次查询就能完成所有批量插入操作,性能比逐行执行高10倍以上,也完全不会占用多余连接。
3. 增加异步流程控制,限制并发数
给循环内的异步操作加await,或者用限流机制控制同时运行的事务数量,避免短时间内同时申请过多连接。
修改后代码示例
// 初始化插入列模板,只需执行一次 const cs = new pgp.helpers.ColumnSet(['id', 'isdeleted', 'parentid'], {table: 'test_data'}); async function call(){ // 可根据业务需要调整并发数,此处为串行执行避免占用过多连接 for (let index = 0; index < 5; index++) { const result = await getPendingData(); await db.tx(async (t) => { // 组装所有待插入的数据 const insertValues = result.map(() => ({ id: uuidv1(), isdeleted: 0, parentid: `${uuidv1()}x-${uuidv1()}` })); // 生成批量插入SQL,执行一次完成所有插入 const insertSql = pgp.helpers.insert(insertValues, cs); await t.none(insertSql); }); } } const getPendingData = async () => { return db.task('getPendingData', async (t) => { return await t.any('select * from other_table'); }); } call();
如果业务场景必须逐行执行插入语句,仅需要把原代码中事务内的db.none改为t.none,也能解决连接耗尽问题,只是性能比批量插入低。
内容的提问来源于stack exchange,提问作者Ronaldo Cristover
相关产品推荐
相关产品推荐

