前言
大家對 樂觀鎖 這三個字眼應該不陌生吧?
為什么今天我想談談樂觀鎖的設計呢?
關于資料庫的樂觀鎖使用, 是不是很多人一看到樂觀鎖就會想到 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 無鎖演算法 大家了解過么?
可以去了解下,再回來 看看 文中說的 資料庫里樂觀鎖的設計思想,
該篇淺談就到這,神神叨叨習慣了,說的東西可能沒營養可能有營養,就到這吧,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/178919.html
標籤:其他
