SQLAlchemy session的autocommit autoflush詳解

v2-35ae7c99c4a63f6924ac1436d3bf4c84_1200x500.jpg

SQLAlchemy 的 session 是指什么

顧名思義本刽,session就是會話,對話的意思固逗,它的作用就是跟數(shù)據(jù)庫DB交互的
我們來看sqlalchemy 的session是如何創(chuàng)建的

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
# or
Session = sessionmaker()
Session.configure(bind=engine)  # once engine is available

# 自定義完Session以后彬坏,我們要實例化Session挟鸠,這里創(chuàng)建了兩個session
session1 = Session()
session2 = Session()

# 之后的增刪改查就是在這個session 對象里面操作了
ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
session1.add(ed_user)

The above Session is associated with our SQLite-enabled Engine, but it hasn’t opened any connections yet. When it’s first used, it retrieves a connection from a pool of connections maintained by the Engine, and holds onto it until we commit all changes and/or close the session object.

session 特點:

  • session的作用就是真正跟database交互的嘉赎,而engine 就是告訴session使用什么引擎去數(shù)據(jù)庫置媳,也就是mysql呢還是sqlite。

  • session 和連接(connection) 不等同公条,session 通過連接和數(shù)據(jù)庫進行通信拇囊。創(chuàng)建完session對象,還沒真正打開數(shù)據(jù)庫連接靶橱,當它第一次使用的時候寥袭,session就會從連接池獲取一個連接來進行跟DB交互,而這個連接池是Engine來維護的抓韩,session之后就會hold住這個連接知道commit 或者 關閉session

  • session 是 Query 的入口纠永,當你想要發(fā)起查詢的時候,一般用法是:session.Query(Model).filter_by(...).first()

至于線程池谒拴,就是在create_engine的時候確定的

session的autoflush的作用

  • flush 的意思就是將當前 session 存在的變更發(fā)給數(shù)據(jù)庫,換句話說涉波,就是讓數(shù)據(jù)庫執(zhí)行 SQL 語句英上。
  • commit 的意思是提交一個事務。一個事務里面可能有一條或者多條 SQL 語句
  • SQLAlchemy 在執(zhí)行 commit 之前啤覆,肯定會執(zhí)行 flush 操作苍日;而在執(zhí)行 flush 的時候,不一定執(zhí)行 commit窗声,這個主要視 autocommit 參數(shù)而定相恃,后面會詳細講

flush的作用就是相當于在開了一個終端,然后開啟了事務start transaction , 把一堆要執(zhí)行的sql發(fā)送到終端笨觅。
假設我們有一個User表:

class User(Base):
    __tablename__  = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String(64))
// 創(chuàng)建了一個對象拦耐,這時,這個對象幾乎沒有任何意義见剩,session 不知道它的存在
>>> user = User(name='hello')
>>> 
// session.add 這個對象之后杀糯,它被 session 放到它的對象池里面去了,但這時不會發(fā)送任何 SQL 語句給數(shù)據(jù)庫苍苞,數(shù)據(jù)庫目前仍然不知道它的存在
>>>  session.add(user)
>>>  
// session.Query 執(zhí)行之前固翰,由于 autoflush 是 True,session 會先執(zhí)行 session1.flush(),然后再發(fā)送查詢語句
// 當 session 進行 flush 操作時骂际,session 會先建立(選)一個和數(shù)據(jù)庫的連接疗琉,然后將創(chuàng)建 user 的 SQL 語句發(fā)送給數(shù)據(jù)庫
// 所以,這個查詢是能查到 user 的
>>> user = session.query(User).filter_by(name='cosven').first()
>>> print user, user.id, user.name
>>> <__main__.User object at 0x7fce4fb663d0> 10 hello

如果 session 的 autoflush 為 False 的話歉铝,session 進行查詢之前不會把當前累計的修改發(fā)送到數(shù)據(jù)庫盈简,而直接發(fā)送查詢語句,所以下面這個查詢是查不到對象的犯戏。

# autoflush 設置成False
SessionNoAutoflush = sessionmaker(bind=engine, autoflush=False)
session3 = SessionNoAutoflush()

>>> user = User(name='hello')
>>>  session3.add(user)
>>> user = session3.query(User).filter_by(name='cosven').first()
>>> print user, user.id, user.name
None
Traceback (most recent call last):
  File "session.py", line 41, in <module>
    print u, u.id, u.name
AttributeError: 'NoneType' object has no attribute 'id'

總結(jié)flush autoflush

  • flush 就是把sql發(fā)給mysql執(zhí)行的送火,相當于在一個終端下,start transaction, 然后把sql在事務里面執(zhí)行先匪,而此時不管最后事務是否commit种吸,insert的時候已經(jīng)占用了一個id。 緊接著查詢這條記錄呀非,也是可以查詢到的坚俗,因為實在同一個事務內(nèi),而且提交了SQL給MYSQL執(zhí)行岸裙。外部再插入數(shù)據(jù)猖败,就會跳過這個id,看上像突然跳過了一下自增id降允。這個有很大作用恩闻,有時候,一個事務內(nèi)剧董,多個操作幢尚,第二個操作依賴于前一個操作的insert_id,這時候在事務你是可以獲取到這個id的(以前一直認為需要第一個操作提交事務后才能獲取到)翅楼,然后事務回滾時尉剩,也能完整回滾。 所以說事務內(nèi)一切都是準備好的毅臊,回滾可以完整回滾
  • autoflush開啟就是理茎,在同一個事務內(nèi),當前操作需要查DB記錄的管嬉,會把之前操作積累的sql自動發(fā)送給mysql執(zhí)行皂林,那么當前的查詢操作就會查到相應的數(shù)據(jù)。如果autoflush關閉了宠蚂,就不會把積累的sql發(fā)送過去式撼,當前的查詢操作也不會查詢的到。

session的autocommit作用

autocommit的意義就是是否自動提交事務求厕,commit就是用來提交事務的著隆。
這個值是關閉的扰楼,那么項目不會自動提交事務,所以默認每個請求開啟了事務美浦,并且要手動提交事務
如果這個值是開啟的弦赖,說明每個sql都是自動提交事務,也就是說不會開啟事務浦辨。mysql里面默認是開啟的蹬竖,說明開啟事務需求手動開啟,begin 或者start transaction

autocommit=False(sqlalchemy默認)就會全部請求默認開始事務流酬。
autocommit=True 這樣就不會開啟事務了币厕。所以需要手動flush修改,告訴mysql執(zhí)行什么

場景 :
flask-sqlalchemy 默認是關閉autocommit的芽腾,所以所有請求都相當于自動開啟了事務旦装,哪怕是查詢的sql也開了。就是因為這個摊滔,項目中出了問題阴绢。我的api項目,基本都是查詢艰躺,沒有寫入的請求呻袭。然而這個項目請求量很大,我有幾次項目需要修改表結(jié)構(gòu)alert增加字段腺兴,結(jié)果一直卡住了左电,導致服務也請求不了,直接掛了页响。
后天排查了泳猬,發(fā)現(xiàn)是因為我的API項目刹孔,每個請求都開了事務翎迁,而且不會主動去commit 或者rollback胆建,那么請求結(jié)束后绽族,事務依然存在連接中卵酪,這時候去alert表烹骨,這個操作需要獲取鎖莫绣,但是由于有事務存在烟瞧,它獲取不到鎖诗鸭,導致在waiting鎖,而外面的請求也需要鎖來開啟事務参滴,這樣就有了死鎖效應了强岸。兩邊都釋放不了。

根本原因就是因為砾赔,就連查詢的請求SQL都開了事務蝌箍,這是根本沒必要的青灼。后面我把項目的session的autocommit改成了True, 就不會開啟事務了妓盲。
而在沒有開事務的情況下杂拨,需要寫入或者修改記錄,則需要調(diào)用session.flush()來生效

場景還原:
session1 開啟事務悯衬,并且查詢team表弹沽,這時的查詢只是快照查,不是當前讀筋粗,理論上不會產(chǎn)生鎖的

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from team;
+------+------+------+----+----+----+----+----+----+----+----+----+----+------+
| h_id | g_id | num  | c2 | c3 | c5 | c6 | c7 | c8 | c9 | d1 | d2 | d3 | d4   |
+------+------+------+----+----+----+----+----+----+----+----+----+----+------+
|    1 |    2 |   40 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 | NULL |
|    2 |    4 |   37 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 | NULL |
|    3 |    4 |   40 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 |  1 | NULL |
+------+------+------+----+----+----+----+----+----+----+----+----+----+------+
3 rows in set (0.00 sec)

session2 , 修改表結(jié)構(gòu)策橘,增加字段,這時候已經(jīng)被卡住了娜亿,

mysql> alter table team add d5 int;

session3 再開一個終端丽已,session3模擬其他用戶的請求, 這時再次select * team 快照讀也卡住了。之后的請求就是這么卡住的暇唾,導致最后的服務崩潰

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from team where id =2
    -> ;

查看processlist;

mysql> mysql> show processlist;
+----+------+-----------+------+---------+------+---------------------------------+-----------------------------+
| Id | User | Host      | db   | Command | Time | State                           | Info                        |
+----+------+-----------+------+---------+------+---------------------------------+-----------------------------+
| 18 | root | localhost | test | Query   |  257 | Waiting for table metadata lock | alter table team add d5 int |
| 19 | root | localhost | test | Sleep   |  263 |                                 | NULL                        |
| 20 | root | localhost | test | Query   |    0 | starting                        | show processlist            |
+----+------+-----------+------+---------+------+---------------------------------+-----------------------------+
3 rows in set (0.00 sec)

id=18的線程促脉,也就是alter表那個線程,在等待鎖策州,此時已經(jīng)鎖住了team表瘸味,其他的線程也不能獲取鎖讀取team表。

總結(jié) 所以不能隨便給項目開啟autocommit够挂,因為很多情況下旁仿,讀請求根本不需要事務。

https://zhuanlan.zhihu.com/p/48994990

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孽糖,一起剝皮案震驚了整個濱河市枯冈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌办悟,老刑警劉巖尘奏,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異病蛉,居然都是意外死亡炫加,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門铺然,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俗孝,“玉大人,你說我怎么就攤上這事魄健「陈粒” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵沽瘦,是天一觀的道長革骨。 經(jīng)常有香客問我农尖,道長,這世上最難降的妖魔是什么苛蒲? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任卤橄,我火速辦了婚禮,結(jié)果婚禮上臂外,老公的妹妹穿的比我還像新娘窟扑。我一直安慰自己,他們只是感情好漏健,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布嚎货。 她就那樣靜靜地躺著,像睡著了一般蔫浆。 火紅的嫁衣襯著肌膚如雪殖属。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天瓦盛,我揣著相機與錄音洗显,去河邊找鬼。 笑死原环,一個胖子當著我的面吹牛挠唆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘱吗,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼玄组,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谒麦?” 一聲冷哼從身側(cè)響起俄讹,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绕德,沒想到半個月后患膛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡耻蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年剩瓶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片城丧。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖豌鹤,靈堂內(nèi)的尸體忽然破棺而出亡哄,到底是詐尸還是另有隱情,我是刑警寧澤布疙,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布蚊惯,位于F島的核電站愿卸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏截型。R本人自食惡果不足惜趴荸,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宦焦。 院中可真熱鬧发钝,春花似錦、人聲如沸波闹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽精堕。三九已至孵淘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歹篓,已是汗流浹背瘫证。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庄撮,地道東北人背捌。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像重窟,于是被迫代替她去往敵國和親载萌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容