最近使用到flask的sqlalchemy,因為flask對sqlalchemy做了一些封裝,加上自己本身對sqlalchemy也不熟悉,用法上走了很多彎路。
因為沒時間去研究sqlalchemy的源碼,所以只能簡單的測試下用法。
1、flask-sqlalchemy是線程安全的
具體可以參考文章 https://blog.csdn.net/luffyser/article/details/89380186
2、每次查詢完以后,記得commit,不然會占用連接池
我在本地做了個簡單的測試,如果單次查詢請求完,不commit的話,連續請求幾次,再發起request就沒有響應了,推測是數據庫連接池沒釋放,被占用。導致請求數據庫掛起,從而導致沒有response。
每次請求完,直接commit,可以解決此問題。
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
3、但是,如果Commit后,查詢結果可能會從緩存中清掉,如果再使用查詢結果的對象,還會再次建立連接查詢。所以還會出現上述連接池耗盡的問題。
從下圖標黃的日志可以看出,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,)
4、但有時候(可能是時間略長些)commit后,再使用查詢結果的對象,可能會出現報錯:Instance <User at 0x32768d0> is not bound to a Session
此種情況可能是因為綁定的session已經被回收,導致無法再進行查詢,所以出錯。
5、綜上所述,為安全起見,需要在查詢結果后,加上 db.session.expunge(ret),斷開查詢結果與session的關系。讓它成為一個本地實體,不會從緩存中清除,使用時候,就不會再查詢。
6、但測試時候發現一個奇怪的問題,另外一個獲取user的接口,和上面的代碼幾乎沒什么區別,居然會自動rollback,這個讓我百思不得其解。
我的邏輯是取到查詢結果(即用戶)后,判斷用戶的狀態字段是否為1,如果為1,就修改用戶屬性,然后commit,如果不是1,就不做操作,也沒有調用rollback。
但是我測試時候發現,如果不是1的時候,它會自動rollback。
或許這是sqlalchemy的高級功能?
7、總結:
1、sqlalchemy的對象實體(model),和session建立了聯系,你get、set這些model的時候,就算已經commit,也會重新自動和數據庫建立連接(get的時候會重新select、set的時候會重新建立連接,等待你提交,如果你不提交,這個連接一直存在,最終會耗盡。),所以要謹慎使用model的字段,除非你確實明白自己在做什么,會發生什么。
2、使用db.session.expunge會切斷實體和session的關系。這個是個不錯的用法。
3、但我還是強烈建議自己再搞一套model,來做業務層邏輯。sqlalchemy的對象實體僅用來做數據庫的操作。這樣會避免很多時候,連接不小心沒釋放的坑。