首頁 >資料庫 >Redis >Redis怎麼使用樂觀鎖保證資料一致性

Redis怎麼使用樂觀鎖保證資料一致性

WBOY
WBOY轉載
2023-06-02 16:52:42738瀏覽

場景

在Redis 中常常會存在這麼一種情況,讀取某一個key 的值,做一些業務邏輯處理,然後根據讀取到的值來計算出一個新的值,重新set 進去。

如果客戶端 A 剛讀取到 key 值,緊接著客戶端 B 就修改這個 key 的值,那麼就會存在並發安全的問題。

問題模擬

假設 Redis Server 有個鍵名為 test 的key,裡面存放的是一個 json 陣列 [1, 2, 3]。

Redis怎麼使用樂觀鎖保證資料一致性

下面讓我們模擬一下,客戶端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」 裡的內容最終如下:

Redis怎麼使用樂觀鎖保證資料一致性

#CAS 來確保資料一致性

##Redis的WATCH指令提供了檢查並設定(CAS)行為,以用於Redis事務。被 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」 的內容如下:

Redis怎麼使用樂觀鎖保證資料一致性

請看我們透過使用WATCH 和TRANACTION 指令,採用CAS 樂觀鎖的方式實現了資料的一致性。

以上是Redis怎麼使用樂觀鎖保證資料一致性的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除