web 項目中 SQLAlchemy session 的生命週期管理

假設已有一個 Session 工廠類:html

lang:python
Session = sessionmaker(bind=some_engine)

那麼 session 實例的生命週期能夠爲:python


###最短模式 —— 每次請求新建一個 session,用完就 closeweb

lang:python
@contextmanager
def get_session_scope():
    try:
        s = Session()
        yield s
        s.commit()
    except:
        s.rollback()
    finally:
        s.close()

這種模式不適合 web 項目或者說其缺點主要在於對 鏈接池(db pool) 的消耗過快。實際使用中發現,被 close 的 session 的 鏈接並無即時返回可用狀態。所以在請求頻繁時,會出現等待鏈接的狀況。sql


###最長模式 —— 全程使用一個 sessionflask

lang:python
session = Session()

這種方式更加不適合 web 項目。由於根據 SQLAlchemy 的文檔描述安全

The Session object is entirely designed to be used in a non-concurrent fashion, which in terms of multithreading means 「only in one thread at a time」.

session 並非線程安全的。這種併發式的 session 使用會致使錯誤。session


###Thread-Local模式 —— 生命週期與 request 同步併發

lang:python
@app.before_request
def init_session():
    g.session = Session()
    
@app.tear_down_request
def close_session():
    g.session.close()

這其實才是最適合 web 項目的 session 管理方式。(僞代碼中沒有寫 commit 和 rollback,可自行添加)這樣即避免了鏈接池的過快消耗,又避免了併發問題。這也是 SQLAlchemy 文檔中推薦的作法。app

實踐上更靠譜的一段代碼多是:線程

lang:python
from sqlalchemy.orm import scoped_session, sessionmaker
from flask import g

Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=g._get_current_object)

更更可靠的一種方法應該是使用官方擴展,如 Flask-SqlAlchemy

相關文章
相關標籤/搜索