commit

作者: 晓函 | 来源:发表于2021-04-06 15:02 被阅读0次

以前一直是flask中用Flask-SqlAlchemy,可是觉得他封装的太多了,不够自由,于是使用了sqlalchemy,更加自由灵活,当然也有很多需要注意的地方。

1、pool_recycle的参数,
pool_recycle 创建的连接超过这个时间,则销毁并建立新连接。
一般设置1800秒就行。
(这个值必须小于mysql的wait_timeout,查询可用show variables like "%timeout%";)
腾讯云数据库wait_timeout是1小时(3600秒),如果pool_recycle你设置2小时,那么到了一小时数据库就会自动kill这个session会话连接,而sqlalchemy的session还不知道,有访问还继续使用这个被kill的连接,导致flask上所有访问数据库都提示错误。


image.png

2、session.remove
一般来说,你需要在访问数据库的时候创建Session,开启Transaction(事务)同时执行查询语句,在所有查询语句执行结束之后关闭Transaction和Session。任何时候,只要一个Session被创建并建立数据库连接,则它必须被关闭从而将数据库连接释放到连接池中,否则连接将永远不会释放直到达到数据库连接的超时时间。

对于Web应用来说,由于一个请求的处理时间足够短,可以更简单地将Session的生命周期同请求的生命周期保持一致,即如果一个请求需要访问数据库,则创建Session,处理完这个请求则关闭Session。

由于Session是必须被关闭的,为了方便的管理Session,保证一个请求内只有一个Session是更好的选择,我们应该使用scoped_session,保证同一个线程内实例化的Session都为同一个对象。
session_factory = sessionmaker(bind=engine)
session = scoped_session(session_factory)
这样,在请求结束时,你只需要关闭一个Session即可。例如你可以在Flask应用中的teardown_request函数中加入一行代码调用session.remove()方法,就可以保证请求中的Session对象被关闭,它会调用Session的close方法,将数据库连接放回连接池并将未commit的Transaction回滚。
所以我们需要在create_app()->init_app()中注册每次请求结束的回调
app.teardown_request(self.teardown_request)
在回调中self.session.remove()

sqlalchemy初始化都一些,完整代码如下:

    def init_app(self,app):
        sqlalchemy_uri = app.config['SQLALCHEMY_DATABASE_URI']
        self.engine = create_engine(sqlalchemy_uri,
                                    #pool_size=30,#线程数
                                    #pool_recycle 连接回收时间(创建超过这个时间,则销毁并重新创建新连接,必须小于mysql的wait_timeout,show variables like "%timeout%";
                                    pool_recycle=1800,
                                    )
        Session = sessionmaker(self.engine)
        #生成session,scoped_session是线程安全的,请求完成后自动回收
        self.session = scoped_session(Session)
        #注册请求结束的回调
        if hasattr(app, 'teardown_appcontext'):
            app.teardown_appcontext(self.teardown)
        else:
            app.teardown_request(self.teardown)

    # 每次请求结束后,session要remove,否则数据库会出现很多未完成事务
    def teardown(self,exception):
        #print("teardown_request")
        if exception:#如果出错,则回滚
            self.session.rollback()

        self.session.remove()

踩坑经过

第一个坑、打开腾讯云数据库就提示,致命,有未完成都事务


image.png

在sql中查询,确实有未完成都事务

select * from information_schema.innodb_trx

原来就是没有每次请求结束都session.remove()

调用scoped_session的remove()方法会调用ScopedSession.close()关闭 session,释放连接资源、把数据库 transaction 状态恢复到初始状态等,最后销毁 session 本身,下回再调用ScopedSession的时候,又会重新创建一个 session 出来。

第二个坑,每次过几小时,flask就报错,重启gunicorn才正常


image.png

原来是pool_recycle太长,超过了mysql都wait_timeout,每次mysql超时后都直接kill这个连接,把pool_recycle改一下,小于wait_timeout就行。
查询wait_timeout:

show variables like "%timeout%";

腾讯云mysql的


image.png

相关文章

网友评论

      本文标题:commit

      本文链接:https://www.haomeiwen.com/subject/dhgskltx.html