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

Peewee与Dash集成疑问:回调函数内数据库连接无法复用的原因

问题:Dash应用中Peewee连接池无法复用连接导致超出最大连接数

在使用Peewee管理数据库的Plotly Dash应用中,出现playhouse.pool.MaxConnectionsExceeded: Exceeded maximum connections.错误,通过PostgreSQL查询可见连接数持续增长:

ariobot=# SELECT count(*) FROM pg_stat_activity where client_port > 0 and application_name = 'test 1';
 count 
-------
    21
(1 row)

复现代码(test1.py)

# test1.py

from peewee import Model, TextField
from playhouse.pool import PooledPostgresqlExtDatabase

db = PooledPostgresqlExtDatabase(
    "postgresql://postgres@127.0.0.1:5432/ariobot", autoconnect=True, application_name='test 1'
)

class BaseModel(Model):
    class Meta:
        database = db

class Test(BaseModel):
    test = TextField()

db.create_tables([Test])

from dash import register_page, dash_table, callback, dcc, Input, Output, html, Dash

app = Dash(__name__)

app.layout = html.Div(
    [
        html.Div(id="value"),
        dcc.Interval(id="db_query_interval", interval=10000, n_intervals=0),
    ]
)

@callback(
    Output("value", "children"),
    Input("db_query_interval", "n_intervals"),
    )
def do_stuff(interval):
    db = Test.select()
    for db in db:
        val = db.test
    return val

if __name__ == "__main__":
    app.run(debug=True)

对比测试(test2.py)

以下循环代码能正常复用连接,符合预期:

# test2.py

from peewee import Model, TextField
from playhouse.pool import PooledPostgresqlExtDatabase

db = PooledPostgresqlExtDatabase(
    "postgresql://postgres@127.0.0.1:5432/ariobot", autoconnect=True, application_name='test 2'
)

class BaseModel(Model):
    class Meta:
        database = db

class Test(BaseModel):
    test = TextField()

db.create_tables([Test])

from time import sleep

while True:
    db = Test.select()
    for db in db:
        val = db.test
    print(val)
    sleep(1)

对应的PostgreSQL查询结果:

ariobot=# SELECT count(*) FROM pg_stat_activity where client_port > 0 and application_name = 'test 2';
 count 
-------
     2
(1 row)

提问:为何test1.py中的数据库连接无法被复用?


原因分析与解决方案

核心原因

  1. 变量名冲突:回调函数中用db = Test.select()覆盖了全局的数据库连接实例,导致无法通过原实例管理连接的归还。
  2. Dash的多线程/多进程环境:Dash默认debug模式会启动reloader(额外进程),且回调可能在不同线程执行。Peewee连接池默认基于线程本地存储,每个线程会创建新连接,但如果连接未被正确归还,就会持续占用直至池上限。
  3. 未显式管理连接生命周期:test2.py是单线程运行,查询后连接会自动归还池;但Dash回调中没有通过上下文管理器或显式调用确保连接归还,导致连接泄漏。

解决方案

1. 修复变量名冲突

回调中不要用db作为查询结果变量,避免覆盖全局数据库实例:

query = Test.select()  # 替换原db = Test.select()

2. 使用上下文管理器确保连接归还

用db.atomic()或db.connection()包裹数据库操作,无论是否异常,连接都会自动归还池:

@callback(
    Output("value", "children"),
    Input("db_query_interval", "n_intervals"),
    )
def do_stuff(interval):
    query = Test.select()
    val = ""
    with db.atomic():
        for row in query:
            val = row.test
    return val

3. 关闭Dash的reloader(debug模式下)

debug模式的reloader会启动额外进程,占用额外连接,可关闭此功能:

if __name__ == "__main__":
    app.run(debug=True, use_reloader=False)

4. 显式释放连接(备选)

如果不使用上下文管理器,可手动获取并释放连接:

@callback(
    Output("value", "children"),
    Input("db_query_interval", "n_intervals"),
    )
def do_stuff(interval):
    conn = db.connection()
    try:
        query = Test.select()
        val = ""
        for row in query:
            val = row.test
        return val
    finally:
        db.release(conn)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:13:19