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。
原因分析
--force-recreate的强制终止行为:该命令会直接给旧容器发送SIGKILL信号强制停止,不给应用留优雅关闭的时间。应用进程被强制杀死后,无法主动关闭数据库连接,PostgreSQL会一直持有这些连接(默认idle_in_transaction_session_timeout为0,即永不自动释放)。- 应用层面未配置优雅关闭: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
相关产品推荐
相关产品推荐

