悲觀鎖,樂觀鎖和redis分布式鎖
??悲觀鎖(Pessimistic Lock)??
??為什么叫 "悲觀"???
因為它 ??"悲觀" 地認為并發操作一定會發生沖突??,所以在操作數據之前,??先加鎖??,確保其他事務無法修改這條數據,直到當前事務完成。
??實現方式??(數據庫層面):
-
SELECT ... FOR UPDATE(MySQL) -
SELECT ... WITH (UPDLOCK)(SQL Server) -
其他數據庫的排他鎖機制
??特點??:
-
??先鎖再操作??,防止并發修改
-
??適用于高并發寫操作??(如搶購、庫存扣減)
-
??可能降低并發性能??(鎖會阻塞其他事務)
??示例??(ThinkPHP):
??樂觀鎖(Optimistic Lock)??
??為什么叫 "樂觀"???
因為它 ??"樂觀" 地認為并發操作不會沖突??,所以 ??不加鎖??,而是在更新時檢查數據是否被修改過(通常用版本號或時間戳)。
??實現方式??:
-
??版本號機制??(
version字段) -
??時間戳機制??(
update_time字段) -
??CAS(Compare-And-Swap)??(如 Redis)
??特點??:
-
??不加鎖,先操作再檢查沖突??
-
??適用于讀多寫少的場景??
-
??沖突時需要重試或回滾??
??示例??(ThinkPHP):
??對比總結??
|
特性 |
悲觀鎖(Pessimistic Lock) |
樂觀鎖(Optimistic Lock) |
|---|---|---|
|
??加鎖方式?? |
先加鎖再操作( |
不加鎖,更新時檢查沖突 |
|
??適用場景?? |
高并發寫操作(如搶購) |
讀多寫少(如文章編輯) |
|
??性能影響?? |
可能降低并發性能(鎖阻塞) |
無鎖,沖突時才處理 |
|
??實現方式?? |
數據庫鎖機制 |
版本號、時間戳、CAS |
|
??沖突處理?? |
其他事務會被阻塞 |
需要重試或回滾 |
??如何選擇???
-
??悲觀鎖??:數據競爭激烈,必須保證數據一致性(如支付、庫存扣減)。
-
??樂觀鎖??:沖突概率低,希望提高并發性能(如文章編輯、評論更新)。
redis分布式鎖:
這段代碼的作用是 ??使用 Redis 的 SETNX(SET if Not eXists)實現分布式鎖??,防止并發重復提交(如重復提現)。具體解析如下:
??代碼解析??
-
??
setLockNx($withdrawLockKey, 30)??-
調用一個自定義函數(可能是封裝了 Redis 的
SETNX命令)。 -
??
$withdrawLockKey??:鎖的唯一標識(如用戶ID + 業務類型,例如withdraw:user123)。 -
??
30??:鎖的自動過期時間(單位:秒),防止鎖未釋放導致死鎖。
-
-
??
!setLockNx(...)??-
如果獲取鎖失敗(返回
false),說明鎖已存在(即當前有其他請求正在處理相同業務)。 -
直接返回錯誤提示,阻止重復操作。
-
-
??
json_exit_Base64(401, ...)??-
返回 HTTP 401 狀態碼和 Base64 編碼的 JSON 錯誤消息(可能是項目約定的通信格式)。
-
提示用戶:"處理中,請勿重復點擊"。
-
-
??注釋說明??
-
//提現鎖,處理完手動解鎖:提示開發者需要在業務邏輯完成后 ??手動釋放鎖??(如調用delLock($withdrawLockKey)),否則鎖會在 30 秒后自動過期。
-
??實現原理(Redis 分布式鎖)??
-
??加鎖??
通過 Redis 的
SETNX命令實現原子性鎖:(注:高版本 Redis 支持
SET $withdrawLockKey 1 NX EX 30一步完成) -
??解鎖??
業務處理完成后需手動刪除 key:
??適用場景??
-
??提現/支付防重??:防止用戶多次點擊導致重復扣款。
-
??秒殺/庫存扣減??:避免超賣問題。
-
??任何需要分布式環境下的互斥操作??(如定時任務防并發執行)。
??注意事項??
-
??鎖的粒度??
-
鎖的 key 需要足夠具體(例如包含用戶ID),避免不同用戶互相阻塞。
-
-
??鎖的過期時間??
-
過期時間(如30秒)需大于業務處理時間,但不宜過長(否則阻塞其他請求)。
-
-
??原子性問題??
-
確保
SETNX + EXPIRE是原子操作(如用 Redis 2.6.12+ 的SET命令帶NX和EX參數)。
-
-
??手動解鎖??
-
如果業務邏輯異常退出,需在
finally塊中釋放鎖,或依賴自動過期。-
- 安全釋放鎖:
-
??解釋
releaseLockNx($withdrawLockKey)的含義??這段代碼的作用是 ??安全釋放 Redis 分布式鎖??,而不僅僅是簡單執行
DEL命令。它通常是一個 ??自定義函數??,封裝了更嚴謹的解鎖邏輯,確保 ??只有鎖的持有者才能釋放鎖??,避免誤刪其他客戶端的鎖。
??為什么不是
delLock($withdrawLockKey)???-
??直接
DEL的問題??如果簡單地用
DEL刪除鎖:-
??風險??:假設客戶端 A 獲取鎖后因某些原因(如網絡延遲、GC 停頓)導致鎖過期,此時客戶端 B 獲取了鎖。如果 A 仍然調用
DEL,會錯誤地刪除 B 的鎖,導致數據不一致。
-
-
??安全釋放鎖的方案??
正確的做法是 ??在釋放鎖時驗證持有者身份??,通常通過以下方式實現:
-
??方案 1:Lua 腳本(原子性操作)??
檢查鎖的值是否匹配當前客戶端的唯一標識(如 UUID),匹配時才刪除:
-
??方案 2:Redis 事務??
使用
WATCH+GET+DEL組合(但不如 Lua 腳本簡潔)。
-
??
releaseLockNx($withdrawLockKey)的典型實現??該函數內部可能包含類似以下邏輯(偽代碼):
??與
delLock($withdrawLockKey)的區別??函數名
行為
安全性
delLock($key)直接刪除 key,不檢查持有者
低(可能誤刪其他客戶端鎖)
releaseLockNx($key)驗證持有者身份后刪除 key
高(避免誤刪)
??為什么項目中選擇
releaseLockNx而非delLock???-
??防御性編程??
防止因客戶端超時、重試等異常場景導致鎖被錯誤釋放。
-
??代碼可讀性??
releaseLockNx明確表達了“安全釋放鎖”的意圖,而delLock可能被誤解為簡單刪除。
-
-
-

浙公網安備 33010602011771號