FastAPI将数据库连接存入app.state时的连接存活与共享问题
问题场景
我使用psycopg2连接postgresql数据库,为避免每次与数据库交互都新建连接,我在服务启动阶段创建单个连接,存储在app.state中,实现代码如下:
def startup(app: FastAPI) -> Callable: """ :param app: FastAPI :return: start_app """ def app_start() -> None: # create a new connection conn = get_conn() # store the connection app.state.conn = conn app.state.cur = cur return app_start # register startup func app.add_event_handler("startup", startup(app))
调用连接的方式:
request.app.state.conn
我有两个疑问:
- 存储在
app.state中的conn实际运行状态是怎样的?该连接是否会持续保持存活?还是会超时自动断开,且FastAPI调用时会自动新建连接? conn变量是否会在多线程之间共享?还是FastAPI会为不同线程分别创建新连接分配?
回答
关于连接存活状态的问题
app.state只是FastAPI提供的一个全局属性挂载容器,不会对挂载的对象做任何额外处理,你存进去的psycopg2连接就是普通的Python连接实例:
- 没有主动触发断开、网络链路正常、PostgreSQL服务端没有主动踢掉空闲连接的前提下,这个连接会一直保持存活,可以正常执行SQL。
- 一旦连接因为服务端空闲超时、防火墙/Nginx等中间节点的空闲连接回收策略断开,FastAPI完全不会感知到这个变化,绝对不会自动帮你新建连接,下次用这个断开的实例执行SQL时会直接抛出
psycopg2.OperationalError类的连接异常。 - 额外提一句你代码里的明显问题:
app.state.cur = cur中的cur变量没有在app_start作用域内定义,启动时会直接抛NameError,而且不建议全局挂载游标对象,游标会保留上次查询的状态,跨请求复用很容易出现结果错乱。
关于多线程共享的问题
app.state是服务进程内的全局单例,你挂载的conn会被该进程内所有处理请求的线程/协程共享,FastAPI不会为不同线程/请求自动创建新连接:
psycopg2的同步连接本身不支持多线程并发操作,多线程同时复用同一个连接执行SQL,会出现查询结果串扰、连接内部状态损坏、事务错乱等问题,生产环境这么写必然出故障。- 就算是异步驱动的数据库连接,同一时间单个连接也只能处理一个查询,并发场景下全局单连接会直接成为性能瓶颈。
可行的修正方案
不要用全局单连接的方案,改用数据库连接池:
- 同步场景下直接使用
psycopg2.pool.ThreadedConnectionPool,在启动事件中初始化连接池挂载到app.state,每个请求进入时从池里申请一个连接,请求处理完成后把连接还给连接池。 - 如果用多worker模式部署FastAPI(比如Gunicorn启动多个UvicornWorker),每个worker是独立进程,会各自初始化自己的连接池,不需要做跨进程的连接共享。
- 成熟的连接池实现一般自带空闲连接校验、断开重连、最大连接数限制能力,不需要你手动处理连接保活、断连重试的逻辑。
内容的提问来源于stack exchange,提问作者IMAPOTATO
相关产品推荐
相关产品推荐

