基于數據庫表(鎖表,很少使用)
最簡單的方式可能就是直接創建一張鎖表,然后通過操作該表中的數據來實現了。當我們想要獲得鎖的時候,就可以在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。 為了更好的演示,我們先創建一張數據庫表,參考如下:
當我們想要獲得鎖時,可以插入一條數據:
當需要釋放鎖的時,可以刪除這條數據:
基于悲觀鎖
悲觀鎖實現思路?
在對任意記錄進行修改前,先嘗試為該記錄加上排他鎖(exclusive locking)。
如果加鎖失敗,說明該記錄正在被修改,那么當前查詢可能要等待或者拋出異常。 具體響應方式由開發者根據實際需要決定。
如果成功加鎖,那么就可以對記錄做修改,事務完成后就會解鎖了。
其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接拋出異常。
以MySQL InnoDB中使用悲觀鎖為例?
要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執行一個更新操作后,MySQL會立刻將結果進行提交。set autocommit=0;
上面的查詢語句中,我們使用了select…for update的方式,這樣就通過開啟排他鎖的方式實現了悲觀鎖。此時在t_goods表中,id為1的 那條數據就被我們鎖定了,其它的事務必須等本次事務提交之后才能執行。這樣我們可以保證當前的數據不會被其它事務修改。
上面我們提到,使用select…for update會把數據給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB默認行級鎖。行級鎖都是基于索引的,如果一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點需要注意。
基于樂觀鎖
樂觀并發控制(又名“樂觀鎖”,Optimistic Concurrency Control,縮寫“OCC”)是一種并發控制的方法。它假設多用戶并發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據后,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。
以使用版本號實現樂觀鎖為例?
使用版本號時,可以在數據初始化時指定一個版本號,每次對數據的更新操作都對版本號執行+1操作。并判斷當前版本號是不是該數據的最新的版本號。
需要注意的是,樂觀鎖機制往往基于系統中數據存儲邏輯,因此也具備一定的局限性。由于樂觀鎖機制是在我們的系統中實現的,對于來自外部系統的用戶數據更新操作不受我們系統的控制,因此可能會造成臟數據被更新到數據庫中。在系統設計階段,我們應該充分考慮到這些情況,并進行相應的調整(如將樂觀鎖策略在數據庫存儲過程中實現,對外只開放基于此存儲過程的數據更新途徑,而不是將數據庫表直接對外公開)。
缺陷
對數據庫依賴,開銷問題,行鎖變表鎖問題,無法解決數據庫單點和可重入的問題。