Redis: Implémenter HGETSET

Créé le 20 oct. 2011  ·  12Commentaires  ·  Source: redis/redis

Nous avons déjà GETSET fonctionnant sur des clés normales, cependant il n'y a pas d'équivalent pour les hachages, même si les champs de hachage prennent également en charge les incréments via HINCRBY. HGETALL pourrait être un ajout intéressant à la fois pour effectuer des opérations de réinitialisation et d'obtention sur les compteurs et pour résoudre les problèmes de concurrence, en particulier dans un environnement de cluster Redis où les commandes rapides uniques doivent être préférées.

Un exemple de GETSET pour éviter un problème de concurrence est le suivant :

  • Nous avons des hachages représentant les utilisateurs.
  • Les utilisateurs ont un karma associé.
  • Nous donnons à un utilisateur un point de karma chaque fois qu'il visite le site, mais pas avant trois heures après la dernière augmentation de karma.

Ainsi, afin de modéliser ce problème, nous pouvons avoir deux champs dans le hachage représentant nos utilisateurs.

  • Un champ est le karma lui-même. Nous l'incrémentons avec HINCRBY.
  • Un champ est la dernière fois que nous avons incrémenté le karma. Ce champ s'appelle karma_incr_time.

L'algorithme naïf serait, en pseudo code :

function increment_karma_if_needed
    redis.hget(user_hash, "karma_incr_time")
    if enough_time_elapsed
        redis.incrby(user_hash,"karma")
        redis.set(user_hash,"karma_incr_time",current_time())
    end
end

Cependant, il existe une condition de concurrence évidente dans le code ci-dessus car deux instances peuvent lire l'ancien champ karma_incr_time en même temps. Les deux augmenteront le karma. Avec HGETALL, cette condition de concurrence peut être corrigée de la manière suivante :

function increment_karma_if_needed
    redis.hget(user_hash, "karma_incr_time")
    if enough_time_elapsed
        incr_time = redis.getset(user_hash,"karma_incr_time",current_time())
        if incr_time is still old enough (was not updated in the meantime)
            redis.incrby(user_hash,"karma")
        end
    end
end

Avec le changement ci-dessus, seule la première instance gagnera, car elle définira le temps d'incrémentation sur une valeur qui est maintenant trop future pour que l'incrémentation soit effectuée.

Veuillez ajouter vos réflexions sur l'ajout ou non de cette fonctionnalité.

WAITING-OP-REPLY state-proposed-feature

Commentaire le plus utile

Bonjour Harachie, puisque HGETSET est prévu pour 2.6, vous avez également des scripts pour faire exactement cela, donc cela ne va pas réellement permettre des choses autrement impossibles, mais peut être un ajout confortable. Avec les scripts, vous devez faire à la place :

HSET foo bar old_value
EVAL "old = redis.call('hget',KEYS[1],ARGV[1]); redis.call('hset',KEYS[1],ARGV[1],ARGV[2]); return old;" 1 foo bar new_value

Tous les 12 commentaires

Salut,

J'aimerais vraiment voir cette fonctionnalité,
car avoir toutes les données utilisateur dans un seul hachage signifie moins de mémoire.
Ce serait un pas vers cela :).
Nous pouvons alors ignorer une clé supplémentaire en dehors du hachage.

Sincères amitiés

Bonjour Harachie, puisque HGETSET est prévu pour 2.6, vous avez également des scripts pour faire exactement cela, donc cela ne va pas réellement permettre des choses autrement impossibles, mais peut être un ajout confortable. Avec les scripts, vous devez faire à la place :

HSET foo bar old_value
EVAL "old = redis.call('hget',KEYS[1],ARGV[1]); redis.call('hset',KEYS[1],ARGV[1],ARGV[2]); return old;" 1 foo bar new_value

Salut antirez,

Oui, tu as raison ;),
mais tout le monde n'est pas dans ce truc de script.

Bien qu'il serait très agréable d'avoir une telle collection de scripts pour montrer comment simuler des "commandes manquantes" comme celle-ci.
Personnellement, je pourrais vivre avec le script.

Les commandes supplémentaires sont plus faciles à utiliser par contre, je pense.

Sincères amitiés

J'ai abandonné l'idée d'implémenter HGETSET pour le moment, puisque Redis 2.6 prendra en charge les scripts, nous pouvons voir si ce type de fonctionnalités deviendra inutile ou non à l'ère des scripts... fermeture pour l'instant.

Je pense que cela pourrait encore être une fonctionnalité intéressante à implémenter au niveau le plus bas possible.

Je suis d'accord que cela serait toujours utile à mettre en œuvre directement dans redis. Dans notre cas, nous souhaitons utiliser Redis pour stocker temporairement les demandes d'authentification d'API pour chaque utilisateur. Chaque minute, nous synchronisons ceci avec MySQL via SCAN pour ces clés et GETSET à 0. HGETSET éliminerait le besoin de SCAN comme sous-ensemble de clés serait très faible par rapport à la DB.

:+1: utiliser un script ou diviser la propriété que je veux en une clé individuelle semble demander beaucoup de travail pour faire quelque chose d'aussi simple. Je pense que cela aurait un impact sur suffisamment d'utilisateurs pour que cela en vaille la peine !

Travailler sur la construction d'un mutex distribué qui pourrait vraiment utiliser un équivalent de hachage GETSET.

HGETSET devrait être implémenté, tant que nous avons HINCR
Très mauvaise décision de ne pas l'implémenter au niveau de la base de données.

Très mauvaise décision de ne pas l'implémenter au niveau de la base de données.

Je pense que souvent, il est facile de voir les fonctionnalités dont vous avez spécifiquement besoin comme évidemment nécessaires pour tout le monde. Pour moi, il semble que la solution LUA à cela soit la voie à suivre pour 99% des utilisateurs. Le seul avantage de l'intégrer à Redis lui-même est qu'il sera plus rapide.

Je peux faire 10 000 opérations "hgetset" en utilisant la solution LUA en .07s sur mon portable, depuis un client PHP.

Dans le cas peu probable où vous auriez un volume si important que cette commande via LUA soit trop lente, sa mise en œuvre est triviale.

https://github.com/michael-grunder/redis/commit/b6009014589d2fa6e209f53931dd7016f48c03be

Acclamations
Mike

peut effectuer 10 000 opérations "hgetset" en utilisant la solution LUA en .07s sur mon ordinateur portable

Ce n'est pas seulement une question de performances, la simplicité d'utilisation est également très importante !

Cette page vous a été utile?
0 / 5 - 0 notes