ホームページ >データベース >Redis >Redis がオプティミスティック ロックを使用してデータの一貫性を確保する方法

Redis がオプティミスティック ロックを使用してデータの一貫性を確保する方法

WBOY
WBOY転載
2023-06-02 16:52:42737ブラウズ

シナリオ

Redisでは、特定のキーの値を読み取り、何らかのビジネスロジック処理を行った後、読み取った値に基づいて新しい値を計算する場面がよくあります。また入って。

クライアント A がキー値を読み取った直後に、クライアント B がキー値を変更した場合、同時実行セキュリティの問題が発生します。

問題シミュレーション

Redis Server に test という名前のキーがあり、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 コマンドCheck and Set (CAS) 動作は、Redis トランザクションで使用するために提供されています。監視されたキーは監視され、変更されているかどうかが検出されます。 EXEC が実行される前に少なくとも 1 つの監視対象オブジェクトが変更された場合、トランザクション全体がキャンセルされ、EXEC はトランザクション実行の失敗を示す Null リプレイを返します。操作を繰り返すだけでよく、この期間中に新たな競争が発生しないことを祈ります。この形式のロックはオプティミスティック ロックと呼ばれ、非常に強力なロック メカニズムです。

それでは、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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。