Dans Redis, il arrive souvent que la valeur d'une certaine clé soit lue, qu'un traitement de logique métier soit effectué, puis qu'une nouvelle valeur soit calculée en fonction de la valeur lue et définie à nouveau.
Si le client A vient de lire la valeur de la clé, puis que le client B modifie la valeur de la clé, il y aura alors un problème de sécurité de concurrence.
Supposons que Redis Server ait une clé nommée test, qui stocke un tableau json [1, 2, 3].
Simulons la situation où le client A et le client B accèdent aux modifications en même temps. Le code est le suivant :
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") }
Le client A est bloqué pendant 2 secondes. , utilisé pour simuler un traitement de logique métier fastidieux. Le client B a accédé à « test » pendant le traitement et a ajouté l'identifiant : 5.
Lorsque le client A termine le traitement de la logique métier fastidieuse, l'identifiant : 4 est ajouté et l'identifiant : 5 sera écrasé.
Le contenu final de "test" est le suivant :
La commande WATCH de Redis fournit un comportement de vérification et de définition (CAS) pour les transactions Redis. Les clés surveillées seront surveillées et il sera découvert si elles ont été modifiées. Si au moins un objet surveillé est modifié avant l'exécution d'EXEC, la transaction entière sera annulée et EXEC renvoie une relecture Null pour indiquer l'échec de l'exécution de la transaction. Il suffit de répéter l'opération et d'espérer qu'il n'y aura pas de nouvelle concurrence pendant cette période. Cette forme de verrouillage est appelée verrouillage optimiste et constitue un mécanisme de verrouillage très puissant.
Alors comment mettre en œuvre CAS ? Il suffit de modifier le code dans la méthode update() de RedisClientA comme suit :
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 } } }
Le contenu final du "test" est le suivant :
On voit que nous utilisons les commandes WATCH et TRANACTION pour implémenter données utilisant la cohérence de verrouillage optimiste CAS.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!