網站首頁 編程語言 正文
場景
在 Redis 中經常會存在這么一種情況,讀取某一個 key 的值,做一些業務邏輯處理,然后根據讀取到的值來計算出一個新的值,重新 set 進去。
如果客戶端 A 剛讀取到 key 值,緊接著客戶端 B 就修改這個 key 的值,那么就會存在并發安全的問題。
問題模擬
假設 Redis Server 有個鍵名為 test 的key,里面存放的是一個 json 數組 [1, 2, 3]。
下面讓我們模擬一下,客戶端 A 與 客戶端 B 同時訪問修改的情況,代碼如下:
客戶端 A:
class RedisClientA(username: String, password: String, host: String, port: Int) { val jedis: Jedis init { val pool = JedisPool(JedisPoolConfig(), host, port) jedis = pool.resource jedis.auth(username, password) } fun update(key: String) { val idStr = jedis.get(key) val idList = Json.decodeFromString<MutableList<Int>>(idStr) // 等待2秒,模擬業務 TimeUnit.SECONDS.sleep(2L) idList.add(4) println("new id list: $idList") jedis.set(key, Json.encodeToString(idList)) } fun getVal(key: String): String? { return jedis.get(key) } } fun main() { val key = "test" val redisClientA = RedisClientA("default", "123456", "127.0.0.1", 6379) redisClientA.update(key) val res = redisClientA.getVal(key) println("res: $res") }
客戶端 B:
class RedisClientB(username: String, password: String, host: String, port: Int) { val jedis: Jedis init { val pool = JedisPool(JedisPoolConfig(), host, port) jedis = pool.resource jedis.auth(username, password) } fun update(key: String) { val idStr = jedis.get(key) val idList = Json.decodeFromString<MutableList<Int>>(idStr) idList.add(5) println("new id list: $idList") jedis.set(key, Json.encodeToString(idList)) } fun getVal(key: String): String? { return jedis.get(key) } } fun main() { val key = "test" val redisClientB = RedisClientB("default", "123456", "127.0.0.1", 6379) redisClientB.update(key) val res = redisClientB.getVal(key) println("res: $res") }
客戶端 A 阻塞了 2 秒,用來模擬耗時業務邏輯的處理。正在處理的時候,客戶端 B 訪問了 “test”,并增加了 id:5。
在客戶端 A 耗時業務邏輯處理完的時候,增加了 id:4,并且會覆蓋掉 id:5。
最終“test” 里的內容最終如下:
CAS 來保證數據一致性
WATCH 命令可以為 Redis 事務提供 check-and-set(CAS)行為。被 WATCH 的鍵會被監視,并會發覺這些鍵是否被改動過了。如果有至少一個被監視的建在 EXEC 執行之前被修改了,那么整個事務都會被取消,EXEC 返回空(Null replay)來表示事務執行失敗。我們只需要重復操作,希望在這個時間段內不會有新的競爭。這種形式的鎖被稱作樂觀鎖,它是一種非常強大的鎖機制。
那么 CAS 的方式如何實現呢?我們只需要把 RedisClientA 的 update() 方法中的代碼修改如下:
fun update(key: String) { var flag = true while (flag) { jedis.watch(key) val idStr = jedis.get(key) val idList = Json.decodeFromString<MutableList<Int>>(idStr) // 等待2秒,模擬業務 TimeUnit.SECONDS.sleep(2L) val transaction = jedis.multi() idList.add(4) println("new id list: $idList") transaction.set(key, Json.encodeToString(idList)) transaction.exec()?.let { flag = false } } }
最終 “test” 的內容如下:
可見我們通過使用 WATCH 和 TRANACTION 命令,采用 CAS 樂觀鎖的方式實現了數據的一致性。
原文鏈接:https://juejin.cn/post/7071181001652699173
相關推薦
- 2022-06-14 C語言詳解冒泡排序實現_C 語言
- 2023-10-16 Nginx啟動,重啟以及基本命令
- 2022-08-16 C#獲取Description特性的擴展類詳解_C#教程
- 2024-07-15 Spring Boot多環境指定yml或者properties
- 2023-04-11 Python單個項目列表轉換為整數的實現_python
- 2022-10-13 Python線性表種的單鏈表詳解_python
- 2022-07-14 C語言每日練習之統計文本單詞數及高頻詞_C 語言
- 2022-06-13 Go語言學習之運算符使用詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支