Python 如何在sqlalchemy中正确关闭mysql连接?

Python 如何在sqlalchemy中正确关闭mysql连接?,python,mysql,flask,sqlalchemy,Python,Mysql,Flask,Sqlalchemy,我想知道在sqlalchemy中关闭所有mysql连接的正确方法是什么。 对于上下文,它是一个Flask应用程序,所有视图共享相同的会话对象 engine = create_engine("mysql+pymysql://root:root@127.0.0.1/my_database") make_session = sessionmaker(bind=engine, autocommit=False) session = ScopedSession(make_session)() 当应用

我想知道在
sqlalchemy
中关闭所有mysql连接的正确方法是什么。 对于上下文,它是一个Flask应用程序,所有视图共享相同的
会话
对象

engine = create_engine("mysql+pymysql://root:root@127.0.0.1/my_database")

make_session = sessionmaker(bind=engine, autocommit=False)

session = ScopedSession(make_session)()
当应用程序被撕下时,
会话
关闭,
引擎
被释放

session.close()
engine.dispose()
但是根据数据库日志,我仍然有很多错误,比如
[Warning]中止了940到db的连接:'master'用户:'root'主机:'172.19.0.7'(读取通信数据包时出错)

我尝试了一些解决方案,包括调用
gc.collect()
engine.pool.dispose()
,但没有成功

我怀疑在幕后仍有一些连接被引擎打开,需要关闭。是否仍要列出引擎打开的所有会话/连接

在花了很多时间之后,任何建议/帮助/指针都将不胜感激!谢谢


注意:
dispose
close
调用的灵感来自于。顺便说一句,“签出”连接是什么?

这可能无法完全回答您的问题,但我一直在使用此方法来确保我的所有会话都已关闭。每个使用会话的函数都会获得提供会话装饰器。注意,
session=None
参数必须存在

e、 g

我看到它在孵化器气流项目中使用,我非常喜欢它

import contextlib
from functools import wraps

...
Session = ScopedSession(make_session)

@contextlib.contextmanager
def create_session():
    """
    Contextmanager that will create and teardown a session.
    """
    session = Session()
    try:
        yield session
        session.expunge_all()
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()


def provide_session(func):
    """
    Function decorator that provides a session if it isn't provided.
    If you want to reuse a session or run the function as part of a
    database transaction, you pass it to the function, if not this wrapper
    will create one and close it for you.
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        arg_session = 'session'

        func_params = func.__code__.co_varnames
        session_in_args = arg_session in func_params and \
            func_params.index(arg_session) < len(args)
        session_in_kwargs = arg_session in kwargs

        if session_in_kwargs or session_in_args:
            return func(*args, **kwargs)
        else:
            with create_session() as session:
                kwargs[arg_session] = session
                return func(*args, **kwargs)

    return wrapper
导入上下文库
从functools导入包装
...
会话=范围会话(生成会话)
@contextlib.contextmanager
def create_会话():
"""
将创建和拆除会话的Contextmanager。
"""
会话=会话()
尝试:
收益期
session.expunge_all()
session.commit()
除:
会话。回滚()
提升
最后:
session.close()
def提供_会话(func):
"""
如果未提供会话,则提供会话的函数装饰器。
如果要重用会话或作为会话的一部分运行函数
数据库事务,则将其传递给函数(如果不是此包装器)
将创建一个并为您关闭它。
"""
@包装(func)
def包装(*args,**kwargs):
arg_session='session'
func_params=func.\u代码\u.co\u变量名
session_in_args=arg_session in func_参数和\
函数参数索引(参数会话)
最后我找到了罪魁祸首:在后台进程中启动的使用连接到数据库的
会话,并且似乎没有正确释放它


此外,我们发现在Flask应用程序中处理sqlalchemy会话的最佳实践是使用
作用域_会话
,并确保在请求结束时对其调用
remove()
(即
appcontext\u teardown
)。这也用于。

签出连接是正在进行的会话等仍在使用的连接,dispose无法处理这些连接。总之,这个问题将受益于一个恰当的回答。@IljaEverilä谢谢!我努力创建了一个小例子来说明这个问题,但没有成功。。。奇怪的是,在一个小示例中,一切都很好,问题只出现在我们的大型代码库中。我怀疑我们在创建和结束会话方面做错了什么,最终导致了这种副作用。在这篇文章中,我实际上在寻找有关sqlalchemy会话的最佳实践(和问题)。您可以使用
AssertionPool
调试签出的连接相关问题。@georgexsh谢谢,我正在使用此池进行调试!谢谢你的装饰!我们的大多数方法已经有了
session
参数,因此我们可以稍后通过注入假会话或禁用提交来测试它,以确保数据在测试中没有被修改。(我们无法在每次测试时重新创建数据库,因为我们直接使用mysql进行测试,而且创建复杂数据库的成本相当高)这正是我一直在寻找的让我的数据库在Python Anywhere处于控制之下的方法!但是,我不得不删除
**provide\u session**
decorator结尾的括号,以使其在我的代码中工作。谢谢你把你的答案放在这里@Sebastian-这至少对我是一个很大的帮助。不客气!你能分享更多关于你是如何发现的吗?@georgexsh我曾尝试使用
AssertionPool
进行调试,但没有发现任何可疑之处。。。然后不知何故,我记得在某个时候,有一个过程从主过程分叉,然后罪犯被发现:)。
import contextlib
from functools import wraps

...
Session = ScopedSession(make_session)

@contextlib.contextmanager
def create_session():
    """
    Contextmanager that will create and teardown a session.
    """
    session = Session()
    try:
        yield session
        session.expunge_all()
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()


def provide_session(func):
    """
    Function decorator that provides a session if it isn't provided.
    If you want to reuse a session or run the function as part of a
    database transaction, you pass it to the function, if not this wrapper
    will create one and close it for you.
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        arg_session = 'session'

        func_params = func.__code__.co_varnames
        session_in_args = arg_session in func_params and \
            func_params.index(arg_session) < len(args)
        session_in_kwargs = arg_session in kwargs

        if session_in_kwargs or session_in_args:
            return func(*args, **kwargs)
        else:
            with create_session() as session:
                kwargs[arg_session] = session
                return func(*args, **kwargs)

    return wrapper