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時也會查詢到這個尚未真正插入數據庫的對象。這個有待探究 ,后續補充
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM