1. 程式人生 > >SQLAlchemy併發寫入引發的思考

SQLAlchemy併發寫入引發的思考

背景

近期公司專案中加了一個積分機制,使用者登入簽到會獲取登入積分,但會出現一種現象就是使用者登入時會增加雙倍積分,然後生成兩個積分記錄。此為問題 

問題分析 

專案採用微服務架構,下圖為積分機制流程

 

  worker通過分析日誌記錄從而判斷使用者當天積分是否增加,進而進行積分增加增添記錄或者無操作。 兩個worker對積分資料庫進行同時寫入,造成積分雙倍增加的情況,那問題找到了,就是對資料庫併發寫入的問題。 解決方法,加鎖 

鎖 

  • 共享鎖 
    • 定義:共享鎖就是允許多個執行緒同時獲取一個鎖,一個鎖可以同時被多個執行緒擁有。
    • 舉例:比如有一個房間,你和你的女朋友都有要是可以進入這個房間,這就是共享鎖,這個房間是你跟你的女朋友共享的 
  • 互斥鎖 
    • 定義:互斥鎖,也稱作獨佔鎖,排它鎖,一個鎖在某一時刻只能被一個執行緒佔有,其它執行緒必須等待鎖被釋放之後才可能獲取到鎖。
    • 舉例:就比如蹲廁所吧,同一時間一個茅坑由你一個人獨佔,這就叫做獨佔鎖,互斥鎖,其他人是不可以用的(當然,特殊情況除外)
  • 樂觀鎖 
    • 總是假設最好的情況,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號機制和CAS演算法實現。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變數類就是使用了樂觀鎖的一種實現方式CAS實現的。 
  • 悲觀鎖 
    • 總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖(共享資源每次只給一個執行緒使用,其它執行緒阻塞,用完後再把資源轉讓給其它執行緒)。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨佔鎖就是悲觀鎖思想的實現。 

當然,這次解決問題採用的是悲觀鎖,互斥鎖   

SQLAlchemy 

專案是採用SQLAlchemy對mysql進行操作,題目是SQLAlchemy引發的思考,當然就少不了SQLAlchemy這個主角。SQLAlchemy 提供 with_for_update 函式 進行 鎖的操作 .
session.Query(User).with_for_update().first()
session.Query(User).with_for_update(read=True).first()

完整形式為:

with_for_update(read=False, nowait=False, of=None)
read
是標識加互斥鎖還是共享鎖. 當為 True 時, 即 for share 的語句, 是共享鎖. 多個事務可以獲取共享鎖, 互斥鎖只能一個事務獲取. 有"多個地方"都希望是"這段時間我獲取的資料不能被修改, 我也不會改", 那麼只能使用共享鎖.
nowait
其它事務碰到鎖, 是否不等待直接"報錯".
of
指明上鎖的表, 如果不指明, 則查詢中涉及的所有表(行)都會加鎖.

補充 

在用SQLAlchemy對資料庫操作的過程中出現這樣一個現象,當在session.query()時,如果之前有session.add()操作,即使尚未進行commit操作,在query時也會查詢到這個尚未真正插入資料庫的物件。這個有待探究 ,後續補充        
參考連結: 

https://blog.csdn.net/yanlinwang/article/details/41172697  http://www.codexiu.cn/python/SQLAlchemy%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/531/#toc26