最近使用到flask的sqlalchemy,由于flask对sqlalchemy作了一些封装,加上本身自己对sqlalchemy也不熟悉,用法上走了不少弯路。python
由于没时间去研究sqlalchemy的源码,因此只能简单的测试下用法。sql
一、flask-sqlalchemy是线程安全的数据库
具体能够参考文章 https://blog.csdn.net/luffyser/article/details/89380186flask
二、每次查询完之后,记得commit,否则会占用链接池缓存
我在本地作了个简单的测试,若是单次查询请求完,不commit的话,连续请求几回,再发起request就没有响应了,推测是数据库链接池没释放,被占用。致使请求数据库挂起,从而致使没有response。安全
每次请求完,直接commit,能够解决此问题。 session
def queryLast(cls): try: ret = db.session.query(cls).order_by(cls.version.desc()).first()
#db.session.expunge(ret) db.session.commit() except: db.session.rollback() ret = None return ret
三、可是,若是Commit后,查询结果可能会从缓存中清掉,若是再使用查询结果的对象,还会再次创建链接查询。因此还会出现上述链接池耗尽的问题。app
从下图标黄的日志能够看出,commit后,使用返回的查询结果时,又执行了一次查询任务,并返回结果,my god!测试
2019-12-10 12:31:21,650 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 2019-12-10 12:31:21,651 INFO sqlalchemy.engine.base.Engine SELECT appversion.id AS appversion_id, appversion.version AS appversion_version, appversion.`appUrl` AS `appversion_appUrl`, appversion.des AS appversion_des, appversion.`createTime` AS `appversion_createTime`, appversion.`lastModiftyTime` AS `appversion_lastModiftyTime`, appversion.type AS appversion_type FROM appversion ORDER BY appversion.version DESC LIMIT %s 2019-12-10 12:31:21,651 INFO sqlalchemy.engine.base.Engine (1,) 2019-12-10 12:31:21,661 INFO sqlalchemy.engine.base.Engine COMMIT
2019-12-10 12:31:21,680 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 2019-12-10 12:31:21,681 INFO sqlalchemy.engine.base.Engine SELECT appversion.id AS appversion_id, appversion.version AS appversion_version, appversion.`appUrl` AS `appversion_appUrl`, appversion.des AS appversion_des, appversion.`createTime` AS `appversion_createTime`, appversion.`lastModiftyTime` AS `appversion_lastModiftyTime`, appversion.type AS appversion_type FROM appversion WHERE appversion.id = %s 2019-12-10 12:31:21,681 INFO sqlalchemy.engine.base.Engine (1,)
四、但有时候(多是时间略长些)commit后,再使用查询结果的对象,可能会出现报错:Instance <User at 0x32768d0> is not bound to a Session spa
此种状况多是由于绑定的session已经被回收,致使没法再进行查询,因此出错。
五、综上所述,为安全起见,须要在查询结果后,加上 db.session.expunge(ret),断开查询结果与session的关系。让它成为一个本地实体,不会从缓存中清除,使用时候,就不会再查询。
六、但测试时候发现一个奇怪的问题,另一个获取user的接口,和上面的代码几乎没什么区别,竟然会自动rollback,这个让我百思不得其解。
个人逻辑是取到查询结果(即用户)后,判断用户的状态字段是否为1,若是为1,就修改用户属性,而后commit,若是不是1,就不作操做,也没有调用rollback。
可是我测试时候发现,若是不是1的时候,它会自动rollback。
或许这是sqlalchemy的高级功能?
七、总结:
一、sqlalchemy的对象实体(model),和session创建了联系,你get、set这些model的时候,就算已经commit,也会从新自动和数据库创建链接(get的时候会从新select、set的时候会从新创建链接,等待你提交,若是你不提交,这个链接一直存在,最终会耗尽。),因此要谨慎使用model的字段,除非你确实明白本身在作什么,会发生什么。
二、使用db.session.expunge会切断实体和session的关系。这个是个不错的用法。
三、但我仍是强烈建议本身再搞一套model,来作业务层逻辑。sqlalchemy的对象实体仅用来作数据库的操做。这样会避免不少时候,链接不当心没释放的坑。