淺談樂觀鎖的設計

2020-10-18 14:00:22

前言

大家對 樂觀鎖  這三個字眼應該不陌生吧?

為什麼今天我想談談樂觀鎖的設計呢? 

關於資料庫的樂觀鎖使用, 是不是很多人一看到樂觀鎖就會想到  Version 欄位 (版本標識)。

 

ps: 其實不是非要新增版本欄位

 

正文

 

樂觀鎖 , Optimistic Concurrency Control (樂觀並行控制),簡稱 OCC 。

樂觀鎖不是一種真正的 ‘ 鎖 ’,而是一種實現鎖效果的 設計思想

樂觀地 認為 並行的操作對資料 不會產生衝突,所以沒有使用 真正的 ‘鎖’ 去對資料加鎖;

而是選擇在提交資料的時候,去檢測資料是否衝突了? 

發現衝突就採取 處理操作,例如報錯、重試、停止等等。

 

設計

基於資料庫使用展開介紹

 

  使用版本標識  version 欄位

 

也就是在 表內 增加一個欄位  version 。 

 每次寫操作如果時成功的,都需要 將 version 版本值 +1 ,

例如原來 某條資料的 version值為  1, 如果修改了,那麼 version就需要變成 version+ 1   , 也就是 2.   

然而在並行場景,大量的寫操作不免會發生衝突。

所以當我們 讀取 資料, 需要做更新操作。 我們的 設計流程時這樣的:

1. 讀取資料,把資料裡的version值 取出作為  更新前標識 值  version-before。

2.做業務邏輯計算等等 .... 

3. 更新資料操作 ,更新時, 將之前的 標識 值    version-before 與 資料庫裡面的  version值  做匹對, 檢測是否一致。

如果一致, 那麼意味著 這時段內,沒有其他寫操作修改過資料, 那麼我們可以提交成功。

如果不一致,那麼意味著 發生了寫寫衝突, 也就是我們此刻需要更新的資料,已經被修改過了。那麼我們可以根據業務場景,採取處理措施 (報錯記錄、重試流程、停止等等)。

ps: 注意了,這裡的讀取,檢測,更新  這些操作都是務必保證 操作的原子性 ,連貫執行,也就是處於同個事務內。

mysql語句的寫法舉例:

update proinfo  set proNum = proNum + 10 , version = version + 1 where version = #{version} and proId = #{proId}

 

只要where 後的 version 條件不成立,那麼就是更新不成功,也就是 檢測到了 ‘衝突’  。

 

 

那麼前言裡,我提到 使用樂觀鎖,不一定非要新增版本 version欄位。

我們還可以使用 updateTimestamp  這種欄位值。

我想,大家接觸過很多專案,是不是看到很多老專案的表內都會有個 更新時間(時間戳)的欄位,

但是好像業務裡又沒有用。

其實,這種欄位,可以用來實現 樂觀鎖。

精確到毫秒或者更細, 每次操作,讀取資料前把 時間戳的值 儲存,然後更新提交的時候, 將這時間戳和資料庫內的時間戳 值做匹對,原理也是一樣的。時間戳欄位可以自己傳入,也可以是通過mysql函數預設獲取更新。

update proinfo set proNum = proNum + 10 , updateTimestamp = unix_timestamp(now())                                                        where  updateTimestamp = #{updateTimestamp} and  proId = #{proId}

(可能有人會反駁,如果時間戳一模一樣呢? 我不多說、)

為什麼需要提這個呢。 因為我想傳達的是, 樂觀鎖,要理解這種 樂觀控制的設計思想,靈活去運用。

而不是固化,看到千篇一律地加欄位 version,就跟著加。

有時候有些老專案,不是說加個欄位那麼回事,也許會引發一些雜七雜八的問題。

那麼我們大可去根據實際情況去設計樂觀鎖。  

 

 

那麼最後也簡單地說下這個資料庫使用樂觀鎖設計,

1.最好是 讀多寫少的場景下使用,因為寫的操作少了,也就更樂觀了。

2. 在發現衝突時, 咱們的處理操作要慎重設計, 特別是寫操作並行特別多的情況,採取 無限制地重試? 短時間會不會適得其反?  

 

 

 

 

 

思想很重要,不是隻顧著去套模式,因為掌握了這種思想,也許你不單單在使用樂觀鎖的時候你才用得上。

 

最後給大家留個小話題, CAS 無鎖演演算法 大家瞭解過麼?

可以去了解下,再回來 看看 文中說的 資料庫裡樂觀鎖的設計思想。

 

該篇淺談就到這,神神叨叨習慣了,說的東西可能沒營養可能有營養,就到這吧。