简体   繁体   English

Flask SQLAlchemy 不会关闭 MySQL 数据库连接

[英]Flask SQLAlchemy does not close MySQL database connections

I have a Flask app using Flask-SQLAlchemy with a MySQL database where the db is defined as the following:我有一个使用 Flask-SQLAlchemy 和 MySQL 数据库的 Flask 应用程序,其中 db 定义如下:

db.py : db.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

main.py : 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 : 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)

I query my database using models using either db.engine.execute() to write raw SQL queries where required or use the integrated Flask-SQLAlchemy APIs for reading data such as User.query.filter_by().all() .我使用模型查询我的数据库,使用db.engine.execute()在需要的地方编写原始 SQL 查询,或者使用集成的 Flask-SQLAlchemy API 来读取数据,例如User.query.filter_by().all()

I write new data into the db using the following:我使用以下命令将新数据写入数据库:

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

I am monitoring my MySQL server using show processlist and I notice that the database connections keep increasing by 2 for every single request that comes my way.我正在使用show processlist监视我的 MySQL 服务器,我注意到数据库连接对于我遇到的每个请求都持续增加 2。 The database connections seem to reset only when I stop the Flask process.数据库连接似乎只有在我停止 Flask 进程时才会重置。 With time, the MySQL server throws the below error:随着时间的推移,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)`

I am serving the app using gunicorn and gevent/eventlet with 2 worker processes.我使用 gunicorn 和 gevent/eventlet 为应用程序提供 2 个工作进程。 I use python3.我使用python3。

Am I missing something here?我在这里错过了什么吗? I tried ending the db session and disposing the engine, but this does not seem to work.我尝试结束数据库会话并处理引擎,但这似乎不起作用。

I finally found a fix to the above problem.我终于找到了解决上述问题的方法。

I used the declarative model defined in here instead of following the quickstart documentation for Flask-SQLAlchemy given here .我使用了此处定义的声明性模型,而不是遵循此处给出的 Flask-SQLAlchemy 快速入门文档。

The changed files are as follows:修改后的文件如下:

db.py : 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 : main.py

from db import init_db, db_session

init_db()

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

user.py : 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)

To query for records we could either use User.query.filter_by().all() or db_engine.execute() .要查询记录,我们可以使用User.query.filter_by().all()db_engine.execute()

To write new data into the database, we can use the following:要将新数据写入数据库,我们可以使用以下命令:

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

In case we need to close session before creating a new child process (what is recommended), this is what we should use:如果我们需要在创建新的子进程之前关闭会话(推荐),这就是我们应该使用的:

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

Like喜欢

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
    '''

Use with statement, there is a test:使用with语句,有一个测试:

def test():
    with db.session() as dbss:
        qss = models.WhateverModel.query.session
        assert dbss == qss

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM