【问题标题】:Flask SQLAlchemy does not close MySQL database connectionsFlask SQLAlchemy 不会关闭 MySQL 数据库连接
【发布时间】:2019-03-30 05:57:29
【问题描述】:

我有一个使用 Flask-SQLAlchemy 和 MySQL 数据库的 Flask 应用程序,其中 db 定义如下:

db.py:

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

main.py:

from db import db
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://" + \
        DB_USERNAME + ":" + DB_PASSWORD + "@" + DB_HOST + "/" + DB_DATABASE
db.init_app(app)

@app.teardown_appcontext
def teardown_db(error):
    db.session.close()
    db.engine.dispose()

user.py:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

我使用模型查询我的数据库,使用 db.engine.execute() 在需要时编写原始 SQL 查询或使用集成的 Flask-SQLAlchemy API 读取数据,例如 User.query.filter_by().all()

我使用以下方法将新数据写入数据库:

new_user_entry = User(username = "abc", email = "abc@example.com")
db.session.add(new_user_entry)
db.session.commit()

我正在使用show processlist 监控我的 MySQL 服务器,我注意到数据库连接对于我遇到的每个请求都会持续增加 2。数据库连接似乎只有在我停止 Flask 进程时才会重置。随着时间的推移,MySQL服务器会抛出以下错误:

`sqlalchemy.exc.TimeoutError: QueuePool limit of size 10 overflow 10 reached, connection timed out, timeout 30 (Background on this error at: http://sqlalche.me/e/3o7r)`

我正在使用带有 2 个工作进程的 gunicorn 和 gevent/eventlet 为应用程序提供服务。我用python3。

我在这里遗漏了什么吗?我尝试结束 db 会话并处理引擎,但这似乎不起作用。

【问题讨论】:

  • 如何查询数据库?请发布...
  • 检查在后台进程中启动的APScheduler。它可能仍在使用连接到数据库的会话而不释放它们。虽然我对 Flask 的经验很少,但一般的经验法则(无论是什么编程语言或框架) - 尝试在会话之间重用/共享连接。
  • @DinkoPehar 用我访问数据库的各种方式更新了问题。
  • 对不起,我对此了解不多,我以为你不知道 session.commit() 。也许这会对flask.pocoo.org/docs/1.0/appcontext/#storing-data 有所帮助。祝你好运
  • @DinkoPehar 我尝试了提供的解决方案,但我无法绕过 db.init_app(app) 被初始化。

标签: python mysql flask sqlalchemy flask-sqlalchemy


【解决方案1】:

我终于找到了解决上述问题的方法。

我使用了here 中定义的声明性模型,而不是遵循给定here 的 Flask-SQLAlchemy 快速入门文档。

修改后的文件如下:

db.py:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(DB_URI, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

def init_db():
    import user
    Base.metadata.create_all(bind=engine)

main.py:

from db import init_db, db_session

init_db()

@app.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()

user.py:

from sqlalchemy import Column, Integer, String
from data_models.db import Base
class User(Base):
    id = db.Column(Integer, primary_key=True)
    username = db.Column(String(80), unique=True, nullable=False)
    email = db.Column(String(120), unique=True, nullable=False)

要查询记录,我们可以使用User.query.filter_by().all()db_engine.execute()

要将新数据写入数据库,我们可以使用以下方法:

new_user_entry = User(username = "abc", email = "abc@example.com")
db_session.add(new_user_entry)
db_session.commit()

【讨论】:

    【解决方案2】:

    如果我们需要在创建新的子进程之前关闭会话(推荐),这是我们应该使用的:

    db.session.remove()
    db.engine.dispose()
    

    喜欢

    from multiprocessing import Process
    from app import db
    
    @app.route('/process/start/', methods = ["GET"])
    def process_start():
        db.session.remove()
        db.engine.dispose()
        p = Process(target = long_task)
        p.start()
        return 'started the long task'
    
    def long_task():
        '''
        do long task
        '''
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-28
      • 2018-06-13
      • 2015-07-28
      • 2021-06-30
      • 2015-09-11
      • 2015-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多