Pour de nombreux développeurs, Redis est synonyme du modèle Cache-Aside : une vérification rapide en base de données, une recherche en mémoire et une écriture en cas de défaut. Bien que efficace pour les charges de travail simples et majoritairement en lecture, les systèmes distribués modernes exigent davantage de leur couche de mise en cache. À mesure que les applications évoluent, le besoin de gestion d'état complexe, de limitation de débit en temps réel et de services basés sur la localisation devient critique. Dans cet article, nous explorons trois modèles Redis avancés qui transforment Redis d'un simple cache en une base de données opérationnelle puissante.
1. Gestion atomique des sessions avec des Hashes
Stocker les sessions utilisateur dans Redis est une pratique courante, mais la manière dont vous structurez ces données est importante. Bien que les chaînes JSON soient populaires, les Hashes Redis offrent des avantages distincts pour la gestion des sessions. Les Hashes permettent de stocker des champs au sein d'une clé, ce qui permet des mises à jour partielles sans récupérer l'intégralité de l'objet. Cela est crucial pour l'atomicité et l'efficacité.
Imaginez un scénario où vous devez mettre à jour l'horodatage de connexion d'un utilisateur sans récupérer et resérialiser l'intégralité de la charge utile de la session. Avec un Hash, vous pouvez mettre à jour un seul champ de manière atomique.
// Pseudocode pour la stratégie de session Redis Hash
# Définir les données de session
redis.HSET("session:user:123", "username", "jdoe", "role", "admin")
redis.HSET("session:user:123", "last_login", "2023-10-27T10:00:00Z")
# Mettre à jour atomiquement l'horodatage sans cycle de lecture/écriture complet
redis.HINCRBY "session:user:123" "view_count" 1
# Définir une expiration pour le nettoyage automatique
redis.EXPIRE "session:user:123", 3600
Cette approche réduit la surcharge réseau et garantit que les mises à jour concurrentes de différents champs de session ne s'écrasent pas les unes les autres, à condition d'utiliser des opérations sur un seul champ.
2. Limitation de débit par seau de jetons (Token Bucket)
La limitation de débit est essentielle pour la sécurité et la stabilité des API. Une approche naïve utilisant des compteurs simples conduit souvent à des conditions de concurrence ou à des problèmes de burst. L'algorithme Token Bucket est supérieur car il permet un taux spécifique de requêtes tout en accommodant des courts pics, à condition que le taux moyen reste dans les limites.
Bien que la mise en œuvre d'un journal de fenêtre glissante véritablement fluide nécessite des scripts Lua complexes, un seau de jetons simplifié peut être géré à l'aide de clés Redis avec expiration. L'idée clé est de stocker l'heure de la dernière demande et la taille du seau autorisé.
lua_script = """
local key = KEYS[1]
local rate = tonumber(ARGV[1]) -- demandes par seconde
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = 1
local fill_time = capacity/rate
local ttl = math.ceil(fill_time)
local last_tokens = redis.call('get', key)
if last_tokens == false then
last_tokens = capacity
end
last_tokens = tonumber(last_tokens)
local refill = math.min(capacity, last_tokens + ((now - last_tokens_time) * rate))
if refill >= requested then
redis.call('set', key, refill - requested, 'EX', ttl)
return 1
else
return 0
end
"""
# Exécuter le script Lua pour l'atomicité
redis.eval(lua_script, 1, "ratelimit:user:api", 10, 100, current_timestamp)
L'utilisation de scripts Lua garantit que la logique de vérification et de mise à jour est atomique, empêchant les conditions de concurrence lorsque plusieurs clients atteignent l'API simultanément.
3. Requêtes géospatiales avec GEOADD
Les services basés sur la localisation ont explosé en popularité. Redis fournit des index géospatiaux intégrés utilisant HyperLogLog et des ensembles triés. En utilisant GEOADD, vous pouvez stocker des paires de latitude et de longitude et les interroger avec une vitesse remarquable.
Ceci est idéal pour trouver des utilisateurs, des restaurants ou des magasins à proximité. La structure de données sous-jacente est un ensemble trié où le score est calculé à partir des coordonnées géographiques, permettant des requêtes de plage efficaces.
# Ajouter des membres à un ensemble géospatial
redis.GEOADD("cafes", -122.423246, 37.779388, "Starbucks")
redis.GEOADD("cafes", -122.44656, 37.78653, "Blue Bottle")
# Trouver des cafés dans un rayon de 1 km d'un point central
redis.GEOSEARCH("cafes",
FROMLONLAT(-122.43, 37.78),
BYRADIUS(1000, "m"),
WITHDIST)
# Sortie : [("Starbucks", 0.55), ("Blue Bottle", 1.2)]
Cette capacité supprime le besoin de requêtes PostGIS PostgreSQL complexes ou d'index géospatiaux MongoDB pour de nombreux cas d'utilisation, offrant des temps de réponse inférieurs à la milliseconde pour les recherches de proximité.
Conclusion
Aller au-delà du modèle de base Cache-Aside nécessite un changement d'état d'esprit. Redis n'est pas seulement une base de données plus rapide ; c'est un outil spécialisé pour des défis spécifiques de concurrence et de structures de données. En tirant parti des Hashes pour l'état atomique, des scripts Lua pour une limitation de débit cohérente et des index Geo pour les données de localisation, vous pouvez construire des applications plus résilientes, évolutives et performantes. À mesure que votre équipe d'ingénierie grandit, investir dans ces modèles avancés rapportera des dividendes sous forme de latence réduite et de fiabilité système améliorée.