樂觀鎖與悲觀鎖的選擇

在資料庫操作時,面對多個操作的並行操作,為了避免資料不一致和衝突我們主要有兩種方法可以達成這個目標:樂觀鎖 (Optimistic Lock) 和悲觀鎖 (Pessimistic Lock)。但我們應該如何選擇適合的方法呢?

樂觀鎖和悲觀鎖的實作網路資源太多了,這篇不會特別提。

樂觀鎖分析

個人經驗來說,會用一個 Version 欄位來記錄版本,在寫入資料時再判斷版本是否因為被其他操作更新過,以此為依據來避免互相覆蓋。

特性

  1. 由應用程式控制而不是資料庫中真實的鎖。
  2. 衝突時比較晚更新的那個操作會被取消,應用程式中可能會拋出例外。

優點

  1. Entity Framework Core 和 NHibernate 等 ORM 有支援樂觀鎖,使用方便。

缺點

  1. 衝突時比較晚更新的那個操作如果一定要更新就需要有重試機制,當衝突率高時重試開銷會很高。
  2. 操作取消並重試的機制在效能上必然較差,重試機制要兼顧資料正確性時較複雜。

適用情境

  1. 衝突時只需要認列第一個操作時。
  2. 衝突時需要認列後面的操作,但是不能直接寫入而需要重新執行一些商務邏輯再更新才正確時。

悲觀鎖分析

悲觀鎖又稱行鎖 (Row Lock),一開始操作時就先把該筆資料鎖定,防止其他操作修改該資料,解鎖後其他操作會繼續執行,它可以預防衝突,而不是在事後解決衝突。

特性

  1. 由資料庫提供的行鎖。
  2. 衝突時所有操作會排隊在解鎖後依序執行。

優點

  1. 避免事後處理衝突的開銷。

缺點

  1. Entity Framework Core 和 NHibernate 等 ORM 不直接支援,如果要使用會需要用純 SQL 下指令,以 Oracle 來說就是 SELECT C1, C2 FROM T1 FOR UPDATE
  2. 鎖定時資料無法被修改,如果因為操作不當而沒有及時解鎖,就會嚴重影響效能。

適用情境

  1. 衝突時多個操作可依序執行時(例如後面的變更覆蓋之前的變更是正確的時)。

結論

這兩種鎖互有優缺,應該要適當的搭配使用。

參考

ChatGPT