In Redis gibt es oft eine Situation, in der der Wert eines bestimmten Schlüssels gelesen wird, eine gewisse Geschäftslogikverarbeitung durchgeführt wird und dann ein neuer Wert basierend auf dem gelesenen Wert berechnet und erneut festgelegt wird.
Wenn Client A gerade den Schlüsselwert gelesen hat und Client B dann den Schlüsselwert ändert, liegt ein Problem mit der Parallelitätssicherheit vor.
Angenommen, der Redis-Server verfügt über einen Schlüssel namens test, der ein JSON-Array [1, 2, 3] speichert.
Simulieren wir die Situation, in der Client A und Client B gleichzeitig auf Änderungen zugreifen. Der Code lautet wie folgt:
Client 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") }
Client 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") }
Client A ist für 2 Sekunden blockiert , wird verwendet, um zeitaufwändige Geschäftslogikverarbeitung zu simulieren. Client B hat während der Verarbeitung auf „test“ zugegriffen und die ID:5 hinzugefügt.
Wenn Client A die Verarbeitung der zeitaufwändigen Geschäftslogik abgeschlossen hat, wird ID:4 hinzugefügt und ID:5 überschrieben.
Der endgültige Inhalt in „Test“ lautet wie folgt:
Der WATCH-Befehl von Redis stellt das Check-and-Set-Verhalten (CAS) für Redis-Transaktionen bereit. Überwachte Schlüssel werden überwacht und es wird festgestellt, ob sie geändert wurden. Wenn mindestens ein überwachtes Objekt geändert wird, bevor EXEC ausgeführt wird, wird die gesamte Transaktion abgebrochen und EXEC gibt eine Null-Wiedergabe zurück, um einen Transaktionsausführungsfehler anzuzeigen. Wir müssen den Vorgang lediglich wiederholen und hoffen, dass es in diesem Zeitraum keine neue Konkurrenz gibt. Diese Form der Verriegelung wird als optimistische Verriegelung bezeichnet und ist ein sehr leistungsfähiger Verriegelungsmechanismus.
Wie implementiert man CAS? Wir müssen nur den Code in der update()-Methode von RedisClientA wie folgt ändern:
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 } } }
Der endgültige „Test“-Inhalt lautet wie folgt:
Es ist ersichtlich, dass wir zur Implementierung die Befehle WATCH und TRANACTION verwenden Daten mit optimistischer CAS-Sperrkonsistenz.
Das obige ist der detaillierte Inhalt vonWie Redis optimistisches Sperren verwendet, um die Datenkonsistenz sicherzustellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!