在資料庫操作時,面對多個操作的並行操作,為了避免資料不一致和衝突我們主要有兩種方法可以達成這個目標:樂觀鎖 (Optimistic Lock) 和悲觀鎖 (Pessimistic Lock)。但我們應該如何選擇適合的方法呢?
樂觀鎖和悲觀鎖的實作網路資源太多了,這篇不會特別提。
樂觀鎖分析
個人經驗來說,會用一個 Version 欄位來記錄版本,在寫入資料時再判斷版本是否因為被其他操作更新過,以此為依據來避免互相覆蓋。
特性
- 由應用程式控制而不是資料庫中真實的鎖。
- 衝突時比較晚更新的那個操作會被取消,應用程式中可能會拋出例外。
優點
- Entity Framework Core 和 NHibernate 等 ORM 有支援樂觀鎖,使用方便。
缺點
- 衝突時比較晚更新的那個操作如果一定要更新就需要有重試機制,當衝突率高時重試開銷會很高。
- 操作取消並重試的機制在效能上必然較差,重試機制要兼顧資料正確性時較複雜。
適用情境
- 衝突時只需要認列第一個操作時。
- 衝突時需要認列後面的操作,但是不能直接寫入而需要重新執行一些商務邏輯再更新才正確時。
悲觀鎖分析
悲觀鎖又稱行鎖 (Row Lock),一開始操作時就先把該筆資料鎖定,防止其他操作修改該資料,解鎖後其他操作會繼續執行,它可以預防衝突,而不是在事後解決衝突。
特性
- 由資料庫提供的行鎖。
- 衝突時所有操作會排隊在解鎖後依序執行。
優點
- 避免事後處理衝突的開銷。
缺點
- Entity Framework Core 和 NHibernate 等 ORM 不直接支援,如果要使用會需要用純 SQL 下指令,以 Oracle 來說就是
SELECT C1, C2 FROM T1 FOR UPDATE
。 - 鎖定時資料無法被修改,如果因為操作不當而沒有及時解鎖,就會嚴重影響效能。
適用情境
- 衝突時多個操作可依序執行時(例如後面的變更覆蓋之前的變更是正確的時)。
結論
這兩種鎖互有優缺,應該要適當的搭配使用。
參考
ChatGPT