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

DigitalOcean托管PostgreSQL连接槽耗尽问题排查求助

解决PostgreSQL连接耗尽问题:旧容器未正常关闭连接导致连接槽占满

问题描述

连接pgAdmin到DigitalOcean托管的PostgreSQL时,反复遇到以下错误:

connection failed: FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute

通过DigitalOcean控制台查看,数据库存在大量未关闭的连接。推测问题出在CD流水线部署环节:每次更新后端代码时,流水线拉取新Docker镜像并执行docker compose up --force-recreate,但旧容器的数据库连接未正常关闭。

CD流水线核心配置如下:

name: Deploy prod stack to DigitalOcean
on:
  create:
    branches:
      - release
  push:
    branches:
      - release
  pull_request:
    branches:
      - release
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checking out the repository
        uses: actions/checkout@v3

      - name: Copy docker-compose-api.prod.yml over to the server
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.PRIVATE_KEY }}
          passphrase: ${{ secrets.PASSPHRASE }}
          source: "docker-compose-api.prod.yml"
          target: /home/${{ secrets.USERNAME }}

      - name: Run the docker-compose-api.prod.yml stack
        uses: appleboy/ssh-action@v0.1.6
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.PRIVATE_KEY }}
          passphrase: ${{ secrets.PASSPHRASE }}
          script: |
            echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            export DATABASE_HOST=${{ secrets.DATABASE_HOST }}
            export DATABASE_PORT=${{ secrets.DATABASE_PORT }}
            export DATABASE_NAME=${{ secrets.SERVER_DATABASE_NAME }}
            export DATABASE_USERNAME=${{ secrets.DATABASE_USERNAME }}
            export DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}
            export RABBITMQ_HOST=${{ secrets.RABBITMQ_HOST }}
            export RABBITMQ_DEFAULT_USER=${{ secrets.RABBITMQ_DEFAULT_USER }}
            export RABBITMQ_DEFAULT_PASSWORD=${{ secrets.RABBITMQ_DEFAULT_PASSWORD }}
            export RABBITMQ_QUEUE=${{ secrets.RABBITMQ_QUEUE }}
            docker compose -f docker-compose-api.prod.yml pull
            docker compose -f docker-compose-api.prod.yml up -d --force-recreate

执行docker ps只能看到新容器运行,旧容器已关闭,但数据库连接仍未释放。服务包含两个应用:一个使用Hibernate(Java),另一个使用Strapi。

原因分析

  1. --force-recreate的强制终止行为:该命令会直接给旧容器发送SIGKILL信号强制停止,不给应用留优雅关闭的时间。应用进程被强制杀死后,无法主动关闭数据库连接,PostgreSQL会一直持有这些连接(默认idle_in_transaction_session_timeout为0,即永不自动释放)。
  2. 应用层面未配置优雅关闭:Hibernate连接池或Strapi未监听进程终止信号,也没有在退出时主动断开数据库连接的逻辑。

解决方案

1. 临时清理积压的连接

先手动释放占用的连接,恢复服务可用性:

-- 查看所有连接详情,确认闲置连接
SELECT pid, usename, application_name, client_addr, state, query_start FROM pg_stat_activity;

-- 终止指定用户的闲置连接(替换为你的应用数据库用户名)
SELECT pg_terminate_backend(pid) FROM pg_stat_activity 
WHERE usename = 'your_app_user' AND state = 'idle';

2. 优化CD流水线的部署流程

替换--force-recreate为优雅停止+启动的流程,给应用足够时间关闭连接:
修改SSH脚本中的部署命令:

# 先优雅停止服务,设置30秒超时(超时后才会强制终止)
docker compose -f docker-compose-api.prod.yml down --timeout 30
# 拉取最新镜像并启动服务
docker compose -f docker-compose-api.prod.yml pull
docker compose -f docker-compose-api.prod.yml up -d

--timeout 30会先给容器发送SIGTERM信号,触发应用的优雅关闭逻辑,30秒后未退出再用SIGKILL强制终止。

3. 配置应用的优雅关闭逻辑

针对Hibernate(Java)服务

  • 如果使用Spring Boot,添加连接池关闭钩子,确保进程退出时关闭所有连接:
    @Bean
    public DisposableBean connectionPoolShutdownHook(DataSource dataSource) {
        return () -> {
            if (dataSource instanceof HikariDataSource) {
                ((HikariDataSource) dataSource).close();
            }
        };
    }
    
  • 确保Docker启动命令直接运行Java进程,不要用sh -c包裹,避免信号被屏蔽(比如启动命令用java -jar app.jar而不是sh -c "java -jar app.jar")。

针对Strapi服务

  • 确保Docker启动命令直接运行Strapi,保证SIGTERM信号能传递到进程;
  • 在Strapi数据库配置中添加连接池超时设置(config/database.js):
    module.exports = ({ env }) => ({
      connection: {
        client: 'postgres',
        connection: {
          // 你的数据库连接配置
          host: env('DATABASE_HOST'),
          port: env.int('DATABASE_PORT'),
          database: env('DATABASE_NAME'),
          user: env('DATABASE_USERNAME'),
          password: env('DATABASE_PASSWORD'),
        },
        pool: {
          min: 2,
          max: 10,
          idleTimeoutMillis: 30000, // 空闲连接30秒后自动关闭
          createTimeoutMillis: 30000,
          acquireTimeoutMillis: 30000
        }
      }
    });
    

4. 配置PostgreSQL自动清理闲置连接

在DigitalOcean数据库控制台的参数设置中调整以下配置,即使应用未优雅关闭,数据库也会自动清理闲置连接:

  • idle_in_transaction_session_timeout: 设置为30000(30秒),自动终止长时间空闲的事务连接;
  • idle_session_timeout: 设置为60000(60秒),终止长时间空闲的非事务连接。

内容的提问来源于stack exchange,提问作者user9132502

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:55:04