Flask对接PostgreSQL出现psycopg2 Too many open files报错如何解决
问题根因
- 服务级文件描述符限制未生效:你修改的
/etc/security/limits.conf仅对PAM登录的用户会话生效,若使用systemd管理gunicorn/nginx服务,或gunicorn启动时未单独配置文件限制,全局配置不会对服务进程生效。 - 原生SQL操作存在连接泄漏:你调用
db.engine.execute()执行原生SQL后仅关闭了结果集,没有显式释放底层数据库连接,虽然配置了NullPool,但每个未释放的连接都会占用一个socket文件描述符,高并发下很快触达上限。 - 多线程下全局Session使用不规范:多线程场景下直接使用全局的
db.session,线程本地的Session如果没有正确销毁,会残留额外的数据库连接。 - (可选)同时使用gunicorn和mod_wsgi会额外增加链路层的资源占用,两者都是WSGI容器,无需同时部署。
修复方案
1. 生效服务级文件描述符限制
- 若使用systemd管理gunicorn服务,在service配置文件(通常路径为
/etc/systemd/system/你的服务名.service)的[Service]段添加如下配置:
LimitNOFILE=500000 LimitNPROC=500000
修改后执行systemctl daemon-reload,再重启gunicorn服务。
- gunicorn启动参数添加
--limit-no-file 500000,确保worker进程继承配置。 - 验证方式:执行
ps aux | grep gunicorn找到worker进程ID,再执行cat /proc/<进程ID>/limits,确认Max open files数值为500000即配置生效。
2. 修复数据库操作逻辑
ORM插入逻辑优化
多线程场景下每次操作单独创建Session,不要复用全局db.session:
from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=db.engine) session = Session() try: user_buy_sell = UserBuySell(app_name=self.app_name.lower(),username=self.user_id) session.add(user_buy_sell) session.commit() finally: session.close()
原生SQL执行逻辑优化
用上下文管理器自动管理连接,避免手动释放遗漏:
with db.engine.connect() as conn: result = conn.execute(read_sql) for res in result: totalCount = int(res['totalcount']) break # 退出上下文时会自动释放连接,无需手动关闭result/conn
原写法中db.engine.execute()是SQLAlchemy旧版本的隐式连接调用,仅关闭结果集不会释放底层连接,是泄漏的核心原因。
3. 可选优化
- 移除mod_wsgi,仅保留gunicorn作为WSGI容器即可,减少不必要的资源占用。
- 若不需要每次新建数据库连接,可将
NullPool替换为固定大小的连接池,配置参考:
SQLALCHEMY_ENGINE_OPTIONS = { 'pool_size': 20, 'max_overflow': 10, 'pool_recycle': 120, 'pool_pre_ping': True }
通过限制最大连接数,从根源避免无限制创建socket。
- 可执行
lsof -p <gunicorn进程ID>查看进程打开的所有文件句柄,确认是否存在其他文件/ socket泄漏。
内容的提问来源于stack exchange,提问作者Navneet Gulati
相关产品推荐
相关产品推荐

