3
votes

Comment compareAndSet fonctionne en interne dans Redis

Le module

spring-data-redis contient la classe RedisAtomicLong .

Dans cette classe, vous pouvez voir

public boolean compareAndSet(long expect, long update) {

    return generalOps.execute(new SessionCallback<Boolean>() {

        @Override
        @SuppressWarnings("unchecked")
        public Boolean execute(RedisOperations operations) {
            for (;;) {
                operations.watch(Collections.singleton(key));
                if (expect == get()) {
                    generalOps.multi();
                    set(update);
                    if (operations.exec() != null) {
                        return true;
                    }
                }
                {
                    return false;
                }
            }
        }
    });
}

Ma question est pourquoi cela fonctionne?

generalOps.multi () démarre la transaction après l'appel de get () . Cela signifie qu'il est possible que deux threads différents (ou même un client) puissent changer de valeur et que les deux réussiront.

operations.watch l'empêche-t-il d'une manière ou d'une autre? JavaDoc n'explique pas le but de cette méthode.

PS: Question mineure: pourquoi pour (;;) ? Il y a toujours une itération.


0 commentaires

3 Réponses :


2
votes

Operations.watch l'empêche-t-il d'une manière ou d'une autre?

OUI. Après avoir observé une clé, si la clé a été modifiée avant la fin de la transaction, EXEC échouera. Donc, si EXEC réussit, la valeur est garantie inchangée par les autres.

pourquoi pour (;;)? Il y a toujours une itération.

Dans votre cas, il semble que la boucle infinie soit redondante.

Cependant, si vous souhaitez implémenter une opération check-and-set pour modifier la valeur avec l'ancienne valeur, le une boucle infinie est nécessaire. Consultez cet exemple de redis doc : p >

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC

Comme EXEC peut échouer, vous devez réessayer l'ensemble du processus en boucle jusqu'à ce qu'il réussisse.


0 commentaires

3
votes

Q: Operations.watch l'empêche-t-il d'une manière ou d'une autre?

<↓OUI.

Citation de la documentation Redis sur la transaction :

WATCH est utilisé pour fournir un comportement check-and-set (CAS) aux transactions Redis.

Les clés surveillées sont surveillées afin de détecter les changements par rapport à elles. Si au moins une clé surveillée est modifiée avant la commande EXEC, toute la transaction est abandonnée et EXEC renvoie une réponse Null pour notifier l'échec de la transaction.

Vous pouvez en savoir plus sur la transaction Redis dans cette documentation.

Q: pourquoi pour (;;)? Il y a toujours une itération.

Il semble que le code que vous avez publié est très ancien. Depuis le cache de Google de cette URL , j'ai vu le code que vous avez fourni qui date du 15 octobre 2012 !

Les derniers codes sont très différents:


0 commentaires

0
votes

L'implémentation de

RedisAtomicLong.compareAndSet n'est pas optimale car elle nécessite 5 requêtes à Redis

Redisson - Le client Redis Java fournit une implémentation plus efficace.

org.redisson.RedissonAtomicLong # compareAndSetAsync méthode implémentée à l'aide du script EVAL atomique :

RAtomicLong atomicLong = redisson.getAtomicLong("myAtomicLong");
atomicLong.compareAndSet(1L, 2L);

Ce script ne nécessite qu'une requête unique à Redis .

Exemple d'utilisation:

  "local currValue = redis.call('get', KEYS[1]); "
+ "if currValue == ARGV[1] "
       + "or (tonumber(ARGV[1]) == 0 and currValue == false) then "
      + "redis.call('set', KEYS[1], ARGV[2]); "
      + "return 1 "
+ "else "
      + "return 0 "
+ "end",


0 commentaires