高负载下PostgreSQL连接报错Cannot assign requested address求助
问题分析与解决方案
首先明确:这个Cannot assign requested address错误不是PostgreSQL连接数耗尽,而是你的Docker容器作为客户端,本地可用的TCP端口被耗尽了——高负载下后台脚本频繁创建、销毁数据库连接,关闭的连接会进入TIME_WAIT状态(默认等待60秒才释放),短时间内大量这类连接堆积,导致容器没有可用端口发起新的数据库连接。
以下是针对性的解决步骤:
1. 恢复并使用连接池,避免频繁创建连接
你之前移除连接池是错误操作,频繁新建/关闭连接正是问题根源:
- 给后台轮询脚本使用长连接:初始化时建立一个数据库连接,轮询过程中复用这个连接,定期检查连接有效性(比如每次轮询前执行
conn.ping()),失效时再重新建立。 - 或者使用psycopg内置的连接池(
psycopg_pool.ConnectionPool),配置固定数量的连接复用,避免每次轮询都新建连接。
示例代码(长连接方式):
import psycopg from psycopg.rows import dict_row import time # 初始化时建立连接 conn = None def get_db_connection(connection_info): global conn if conn is None or conn.closed: conn = psycopg.connect(conninfo=connection_info, autocommit=True, row_factory=dict_row) # 检查连接是否有效 try: conn.execute("SELECT 1") except psycopg.OperationalError: conn = psycopg.connect(conninfo=connection_info, autocommit=True, row_factory=dict_row) return conn # 轮询逻辑 def main(): connection_info = "your_connection_string" while True: conn = get_db_connection(connection_info) # 执行查询逻辑 with conn.cursor() as cur: cur.execute("SELECT * FROM pending_rows LIMIT 100") rows = cur.fetchall() # 处理数据 # 适当休眠,避免高频轮询 time.sleep(1)
2. 调整Docker容器的TCP参数,加速端口复用
通过修改内核参数,让系统更快复用TIME_WAIT状态的端口:
- 启动容器时添加sysctl参数:
docker run --sysctl net.ipv4.tcp_tw_reuse=1 --sysctl net.ipv4.tcp_fin_timeout=30 ...tcp_tw_reuse=1允许复用TIME_WAIT端口,tcp_fin_timeout=30把连接关闭后的等待时间从60秒改成30秒。 - 或者在容器内修改
/etc/sysctl.conf,添加以下内容后执行sysctl -p生效:net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30
3. 替换轮询为PostgreSQL LISTEN/NOTIFY机制,彻底减少连接次数
轮询是低效的,改用PG的异步通知功能,只有当有新数据时才触发查询:
- 在Flask接口的插入逻辑后,添加通知:
# 插入数据后执行 with psycopg.connect(conninfo=connection_info) as conn: with conn.cursor() as cur: cur.execute("NOTIFY new_pending_rows;") - 后台脚本建立长连接,监听通知,收到通知后再查询数据:
def main(): connection_info = "your_connection_string" with psycopg.connect(conninfo=connection_info, autocommit=True) as conn: with conn.cursor() as cur: cur.execute("LISTEN new_pending_rows;") print("Listening for new rows...") while True: # 等待通知 conn.wait() # 收到通知后查询处理数据 cur.execute("SELECT * FROM pending_rows LIMIT 100") rows = cur.fetchall() # 处理数据逻辑
这种方式不需要频繁轮询,全程复用一个长连接,彻底解决端口耗尽问题。
4. 修正PostgreSQL配置的不合理项
你之前调大max_connections到9000是没必要的,反而会占用大量内存导致PG性能下降:
- 把
max_connections调回合理值(比如500-1000),因为问题根本不是PG连接数不够。 - 确认
postgresql.conf中listen_addresses = '*'(允许所有IP连接),pg_hba.conf中添加容器所在网段的访问规则(比如host all all 172.17.0.0/16 md5,对应Docker默认网桥网段)。
5. 验证问题根源
在容器内执行ss -s查看TCP连接状态,重点看TIME-WAIT的数量,如果数值很大(比如上万),就确认是端口耗尽的问题。
在PG中执行SELECT count(*) FROM pg_stat_activity;,如果当前连接数远低于max_connections,说明PG连接数没有耗尽,进一步验证问题出在客户端端口。
内容的提问来源于stack exchange,提问作者Maheedhar
相关产品推荐
相关产品推荐

