You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Promise.all()调用Node-Postgres查询触发连接超时错误

Postgres连接超时问题分析与解决(Promise.all并行调用场景)

错误原因

1. 连接池资源竞争导致超时

你的connectDB函数每次调用都会从连接池获取一个客户端实例。当用Promise.all并行执行两个查询函数时,两个请求会同时向连接池申请客户端:

  • 串行调用时,第一个函数执行完成并释放客户端后,第二个函数才会获取资源,不会触发等待;
  • 并行调用时,若连接池的max(最大连接数)配置过小,第二个请求会进入等待队列,当等待时间超过连接池的超时限制时,就会抛出连接超时错误。

2. 严重的SQL注入风险

你的查询使用字符串拼接WHERE email = '${email}'构造SQL语句,这会导致恶意用户通过构造特殊邮箱值执行任意SQL,直接威胁数据库安全。


解决办法

方案1:优化连接使用逻辑(推荐)

pg库推荐直接使用pool.query()方法,它会自动处理连接的获取与释放,无需手动管理客户端,从根源避免连接竞争问题,同时解决SQL注入风险:

改造db.service

移除手动获取客户端的connectDB函数,保留连接池实例即可:

import { Pool } from 'pg';

const dbConfig = {/* 你的有效数据库配置 */};

const pool = new Pool(dbConfig);

export default pool;

改造查询函数

使用参数化查询替代字符串拼接,同时直接调用pool.query():

// fetchUser.js
import pool from './db.service';

export const fetchUser = async (email) => {
  const query = `
    SELECT full_name FROM app.users
    WHERE email = $1
  `;
  try {
    const result = await pool.query(query, [email]);
    return result.rowCount ? result.rows[0].full_name : null;
  } catch (error) {
    console.error('查询用户失败:', error);
    throw error; // 不要吞掉错误,便于上层捕获处理
  }
};
// fetchWallet.js
import pool from './db.service';

export const fetchWallet = async (email) => {
  const query = `
    SELECT wallet_money FROM app.user_wallet
    WHERE email = $1
  `;
  try {
    const result = await pool.query(query, [email]);
    return result.rowCount ? result.rows[0].wallet_money : null;
  } catch (error) {
    console.error('查询钱包失败:', error);
    throw error;
  }
};

改造后再用Promise.all并行调用,连接池会自动分配可用资源,不会出现超时问题。

方案2:调整连接池配置(临时缓解)

如果暂时无法改造代码,可以增大连接池的最大连接数,并设置合理的超时时间:

const dbConfig = {
  // 原有配置
  max: 20, // 根据业务并发需求调整,默认值为10
  connectionTimeoutMillis: 5000 // 设置连接等待超时时间,单位毫秒
};

方案3:复用单个客户端(适用于事务场景)

如果需要在同一个连接中执行多个操作(比如事务),可以手动获取一次客户端,完成所有操作后再释放:

import pool from './db.service';

export const fetchUserAndWallet = async (email) => {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const userRes = await client.query('SELECT full_name FROM app.users WHERE email = $1', [email]);
    const walletRes = await client.query('SELECT wallet_money FROM app.user_wallet WHERE email = $1', [email]);
    await client.query('COMMIT');
    return {
      fullName: userRes.rows[0]?.full_name,
      walletMoney: walletRes.rows[0]?.wallet_money
    };
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release(); // 必须释放客户端回连接池
  }
};

内容的提问来源于stack exchange,提问作者Kgn-web

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:10:21