原文:https://zhuanlan.zhihu.com/p/48994990
作者:Cosven
來源:知乎
這篇文章致力於解決以下疑問(本文以 MySQL 為例):
- SQLAlchemy 的 session 是指什麼?
- session 的 autoflush 引數是幹什麼的,我到底要不要開啟它?
- session 的 autocommit 引數又是什麼,它和 autoflush 的區別是什麼?
- SQLAlchemy 是在何時傳送 SQL 語句的?
附:
- SQLAlchemy MySQL 除錯小技巧
SQLAlchemy 基礎
下面是一段官方 SQLAlchemy 使用示例,我們從這個例子出發,認識 SQLAlchemy。
from sqlalchemy import create_engine from sqlalchemy import Column, Integer, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # sqlite3/mysql/postgres engine # 請先自己在 MySQL 中建立一個名為 test_tmp 的 database engine = create_engine('mysql://root@localhost/test_tmp', echo=False) Base = declarative_base() Session = sessionmaker(bind=engine) session1 = Session() session2 = Session() SessionNoAutoflush = sessionmaker(bind=engine, autoflush=False) session3 = SessionNoAutoflush() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(64))
session 是什麼?
目前還不知道怎樣直接給 session 下定義,但是我們可以透過它的一些用途來認識它, 在腦海裡腦補出這個東西。
- session 會在需要的時候(比如使用者讀取資料、更新資料時)和資料庫進行通訊,獲取資料物件,並有一個池子來維護這些物件,保證你訪問資料時不出現意外的問題
- session 和連線(connection) 不等同,session 透過連線和資料庫進行通訊
- session 是 Query 的入口,當你想要發起查詢的時候,一般用法是:
session.Query(Model).filter_by(...).first()
如果不完全理解它,也沒關係,有個大概印象即可,以後碰到具體的問題再具體分析, 到時候就可以針對性解決。
官方介紹 session 的資料:https://docs.sqlalchemy.org/en/20/orm/session_basics.html#what-does-the-session-do
autoflush 引數
首先,學習兩個概念:flush 和 commit。
- flush 的意思就是將當前 session 存在的變更發給資料庫,換句話說,就是讓資料庫執行 SQL 語句。
- commit 的意思是提交一個事務。一個事務裡面可能有一條或者多條 SQL 語句
- SQLAlchemy 在執行 commit 之前,肯定會執行 flush 操作;而在執行 flush 的時候,不一定執行 commit,這個主要視 autocommit 引數而定,後面會詳細講
當 autoflush 為 True 時(預設是 True),session 進行查詢之前會自動把當前累計的修改傳送到資料庫(注意:autoflush 並不是說在 session.add 之後會自動 flush),舉個例子(結合開始的程式碼):
# 建立了一個物件,這時,這個物件幾乎沒有任何意義,session 不知道它的存在 >>> user = User(name='cosven') >>> # session1.add 這個物件之後,它被 session 放到它的物件池裡面去了,但這時不會傳送任何 SQL 語句給資料庫,資料庫目前仍然不知道它的存在 >>> session1.add(user) >>> # session1.Query 執行之前,由於 autoflush 是 True,session1 會先執行 session1.flush(),然後再傳送查詢語句 # 當 session 進行 flush 操作時,session 會先建立(選)一個和資料庫的連線,然後將建立 user 的 SQL 語句傳送給資料庫 # 所以,這個查詢是能查到 user 的 >>> session1.query(User).filter_by(name='cosven').first() <__main__.User object at 0x1108f04e0>
如果 session 的 autoflush 為 False 的話,session 進行查詢之前不會把當前累計的修改傳送到資料庫,而直接傳送查詢語句,所以下面這個查詢是查不到物件的。
>>> session3.add(User(name='haha')) >>> session3.query(User).filter_by(name='haha').first() # None
再重複的總結一下:
session.flush 的意義:session 計算自己積累的變更,將變更對應的 SQL 語句傳送給資料庫。 autoflush 的意義:session 在進行查詢之前,自動的進行一次 flush 操作。
autocommit 引數
commit 對應的概念是事務(transaction),預設情況下,session 引數 autocommit 的值是 False,SQLAlchemy 也推薦將它設定為 False。
注:MySQL client 預設是將 autocommit 設為 True 的,所以我們在 cli 中執行一條 SQL 語句,資料庫的資料就會發生變化
這裡複習一下一個基礎知識點:在一個事務被提交之前,事務裡面的修改只對當前事務可見,其它事務看不見。什麼意思?我們看個例子
# ps: session1 的 autocommit 引數為 False, autoflush 引數為 True # 當 session1 執行 add 操作時, >>> session1.add(User(name='miao')) # session1 中是可以查到這個 user 的 >>> session1.query(User).filter_by(name='miao').first() <__main__.User object at 0x1108f00000> # session3 中查不到 >>> session3.query(User).filter_by(name='miao').first() # None # 讓 session1 提交一下當前的事務 >>> session1.commit() # 再從 session3 中查 >>> session3.query(User).filter_by(name='miao').first() is not None True
事務不僅可以提交,還可以 rollback,這裡就不講。
SQLAlchemy MySQL 除錯小技巧
為 MySQL 開啟查詢 log
SET GLOBAL log_output = "FILE"; the default. SET GLOBAL general_log_file = "/path/to/your/mysql.log"; SET GLOBAL general_log = 'ON';
然後在 shell 中 tail -f mysql.log
,這樣一來,當 MySQL 收到請求時,你就能看到一條日誌, 這樣可以方便你判斷 session 執行什麼操作時,會傳送 SQL 語句,什麼時候建立連線。
日誌示例:
2018-11-08T15:12:41.332513Z 53 Query commit 2018-11-08T15:12:41.333753Z 53 Query rollback 2018-11-08T15:12:45.999996Z 43 Query select * from user
將上面的指令碼匯入 python 或者 ipython
python -i test.py