Redisでは、特定のキーの値を読み取り、何らかのビジネスロジック処理を行った後、読み取った値に基づいて新しい値を計算する場面がよくあります。また入って。
クライアント A がキー値を読み取った直後に、クライアント B がキー値を変更した場合、同時実行セキュリティの問題が発生します。
Redis Server に test という名前のキーがあり、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」の最終的な内容は次のとおりです:
#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」の最終的な内容は次のとおりです:
# #WATCH および TRANACTION コマンドは、データの一貫性を達成するために CAS オプティミスティック ロックを使用していることがわかります。
以上がRedis がオプティミスティック ロックを使用してデータの一貫性を確保する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。