Redis如何使用樂(lè)觀鎖(CAS)保證數(shù)據(jù)一致性
場(chǎng)景
在 Redis 中經(jīng)常會(huì)存在這么一種情況,讀取某一個(gè) key 的值,做一些業(yè)務(wù)邏輯處理,然后根據(jù)讀取到的值來(lái)計(jì)算出一個(gè)新的值,重新 set 進(jìn)去。
如果客戶端 A 剛讀取到 key 值,緊接著客戶端 B 就修改這個(gè) key 的值,那么就會(huì)存在并發(fā)安全的問(wèn)題。
問(wèn)題模擬
假設(shè) Redis Server 有個(gè)鍵名為 test 的key,里面存放的是一個(gè) json 數(shù)組 [1, 2, 3]。
下面讓我們模擬一下,客戶端 A 與 客戶端 B 同時(shí)訪問(wèn)修改的情況,代碼如下:
客戶端 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秒,模擬業(yè)務(wù) 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 秒,用來(lái)模擬耗時(shí)業(yè)務(wù)邏輯的處理。正在處理的時(shí)候,客戶端 B 訪問(wèn)了 “test”,并增加了 id:5。
在客戶端 A 耗時(shí)業(yè)務(wù)邏輯處理完的時(shí)候,增加了 id:4,并且會(huì)覆蓋掉 id:5。
最終“test” 里的內(nèi)容最終如下:
CAS 來(lái)保證數(shù)據(jù)一致性
WATCH 命令可以為 Redis 事務(wù)提供 check-and-set(CAS)行為。被 WATCH 的鍵會(huì)被監(jiān)視,并會(huì)發(fā)覺這些鍵是否被改動(dòng)過(guò)了。如果有至少一個(gè)被監(jiān)視的建在 EXEC 執(zhí)行之前被修改了,那么整個(gè)事務(wù)都會(huì)被取消,EXEC 返回空(Null replay)來(lái)表示事務(wù)執(zhí)行失敗。我們只需要重復(fù)操作,希望在這個(gè)時(shí)間段內(nèi)不會(huì)有新的競(jìng)爭(zhēng)。這種形式的鎖被稱作樂(lè)觀鎖,它是一種非常強(qiáng)大的鎖機(jī)制。
那么 CAS 的方式如何實(shí)現(xiàn)呢?我們只需要把 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秒,模擬業(yè)務(wù) 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” 的內(nèi)容如下:
可見我們通過(guò)使用 WATCH 和 TRANACTION 命令,采用 CAS 樂(lè)觀鎖的方式實(shí)現(xiàn)了數(shù)據(jù)的一致性。
到此這篇關(guān)于Redis如何使用樂(lè)觀鎖(CAS)保證數(shù)據(jù)一致性的文章就介紹到這了,更多相關(guān)Redis 樂(lè)觀鎖保證數(shù)據(jù)一致性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis常用數(shù)據(jù)類型命令實(shí)例匯總
這篇文章主要介紹了Redis常用數(shù)據(jù)類型命令實(shí)例匯總,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Redis?定長(zhǎng)隊(duì)列探索及實(shí)踐
這篇文章主要介紹了Redis?定長(zhǎng)隊(duì)列探索及實(shí)踐,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08Redis中3種特殊的數(shù)據(jù)類型(BitMap、Geo和HyperLogLog)
這篇文章主要給大家介紹了關(guān)于Redis中3種特殊的數(shù)據(jù)類型(BitMap、GEOADD和GEODIST)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03Redis在項(xiàng)目中的使用(JedisPool方式)
項(xiàng)目操作redis是使用的RedisTemplate方式,另外還可以完全使用JedisPool和Jedis來(lái)操作redis,本文給大家介紹Redis在項(xiàng)目中的使用,JedisPool方式,感興趣的朋友跟隨小編一起看看吧2021-12-12讓Redis在你的系統(tǒng)中發(fā)揮更大作用的幾點(diǎn)建議
Redis在很多方面與其他數(shù)據(jù)庫(kù)解決方案不同:它使用內(nèi)存提供主存儲(chǔ)支持,而僅使用硬盤做持久性的存儲(chǔ);它的數(shù)據(jù)模型非常獨(dú)特,用的是單線程。另一個(gè)大區(qū)別在于,你可以在開發(fā)環(huán)境中使用Redis的功能,但卻不需要轉(zhuǎn)到Redis2014-06-06redis-benchmark并發(fā)壓力測(cè)試的問(wèn)題解析
這篇文章主要介紹了redis-benchmark并發(fā)壓力測(cè)試的問(wèn)題解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01