Lorsque l'on parle de bases de données, on pense assez logiquement à MySQL, PostgreSQL, SQL Server, Oracle et pourquoi pas à MariaDB, MongoDB ou Cassandra. Mais il en est une, un peu spéciale, qui s'est fait remarquer ces dernières années : Redis. Beaucoup utilisée comme cache, elle a bien d'autres atouts.
Redis, pour REmote DIctionnary Server, est un système de gestion de base de données (SGBD) créé en 2009 par Salvatore Sanfilippo. Il s'agit d'un outil open source (licence BSD-3), développé en C, pensé avant tout pour la performance et pour cause : par défaut, il utilise la mémoire du système comme espace de stockage principal.
Utiliser la mémoire pour des millions d'opérations par seconde
Cette dernière a l'avantage d'être très rapide, avec des débits de plusieurs centaines de Go/s sur des systèmes à huit canaux. Mais aussi en latence, qui est de l'ordre de quelques dizaines de nanosecondes. Là où les meilleurs SSD PCIe/NVMe culminent à 8 Go/s et quelques dizaines de millisecondes, à quelques exceptions près.
Bref, ça va vite et c'est très efficace. Mais il y a un prix à payer : les données en mémoire disparaissent lorsque le système redémarre, elles ne sont pas écrites définitivement. Ainsi, Redis est le plus souvent utilisé pour des usages où il faut avant tout de la performance mais où la durabilité des données n'est pas le critère essentiel.
Sur le site officiel, quelques cas d'usages sont évoqués comme la détection de fraude, la gestion d'inventaire, les statistiques et classements, la messagerie entre des services ou la déduplication. « Redis Enterprise a été soumis à un banc d’essai de traitement de plus de 200 millions d’opérations de lecture/écriture par seconde avec des latences inférieures au millième de seconde, avec seulement une grappe de 40 nœuds sur AWS. Ceci fait de Redis Enterprise la base de données NoSQL la plus efficace en matière de ressources du marché » ajoute l'équipe.
Un cache à tout faire
De manière plus générale, on trouve Redis utilisé comme une sorte de cache, à la manière d'un memcached : mis en relation avec un système de fichiers ou une base de données classiques, il permet de placer facilement une donnée dans la mémoire à son premier accès et de la fournir par son biais ensuite, avec une très faible latence.
Redis est aidé en cela par les Streams, mis en place dans la version 5.0 afin d'assurer ce genre de lien permanent. Mais surtout par son modèle clé/valeur et sa simplicité qui en font un outil facile à prendre en main et sont à la source d'un écosystème qui est devenu assez complet en 12 ans. De très nombreuses bibliothèques permettent d'utiliser Redis dans à peu près tous les langages, des modules, extensions et plugins sont présents pour des tas d'applications, tant pour des usages triviaux comme un blog Wordpress que dans le HPC.
Il est ainsi devenu central dans les architectures en micro-services et bénéficie à plein régime de la montée en puissance du cloud avec l'accès aisé à des instances équipées de larges quantités de mémoire. Mais il a aussi été complété par cet écosystème qui, tout en le gardant simple, en a fait un outil très puissant.
Il est actuellement le 6e SGBD le plus populaire selon le classement de DB-Engines Ranking, le 1er dans le domaine des modèles clé-valeur. Voyons donc ce qu'il permet dans la pratique, avec quelques exemples.
De simples requêtes TCP, bien documentées
Lorsque vous téléchargez Redis, vous installez en réalité un ensemble d'outils, dont un client et un serveur exploitant des sockets TCP sur le port 3739, qui se veut très simple dans ses échanges : vous lui envoyez des commandes sous la forme de texte ; il envoie, de manière très rapide, des réponses sous la forme de texte.
Si vous le souhaitez, une couche de chiffrement peut être utilisée. Vous pouvez aisément monter des clusters et fournir un service haute-disponibilité : vous vous assurez ainsi à travers plusieurs instances et serveurs que vos données seront accessibles même si certains d'entre eux venaient à tomber en panne.
Les liens ci-dessus vous donnent un aperçu d'un autre élément qui fait la force de Redis : sa documentation est complète, détaillée et saura en général vous apporter les réponses que vous cherchez. C'est l'avantage d'un outil porté par une large communauté. Mais c'est aussi une entreprise commerciale qui fournit un service dédié aux entreprises, avec des fonctionnalités spécifiques, mais aussi une offre d'accès dans le cloud.
Cela a d'ailleurs été à l'origine d'un fork, KeyDB. Il a été créé en réponse à certains choix techniques qui déplaisaient à ses initiateurs, comme le fait que Redis soit fondamentalement mono-thread, nécessitant l'utilisation de plusieurs instances pour profiter à plein régime des performances d'un même CPU. Mais aussi pour débrider certaines fonctionnalités réservées aux « pros » comme le très intéressant Redis on Flash (RoF).
Il permet en effet d'utiliser du stockage flash comme périphérique de stockage principal en complément de la mémoire. Une pratique qui a du sens lorsque vous avez besoin de plusieurs To de données, renforcée dans sa pertinence lorsque vous utilisez des SSD comme les Optane d'Intel ou ses modules DIMM PMem qui reposent sur la même technologie, car ils offrent une latence très réduite pour l'accès à de petites quantités données.
Dans la suite de cet article, nous ne traiterons pas de KeyDB, mais vous trouverez sa documentation ici.
Installer et lancer redis, effectuer vos premières requêtes
Logiciel open source oblige, vous pouvez récupérer le code source de Redis et le compiler. De nombreuses distributions le proposent néanmoins dans leurs dépôts. Dans notre cas nous l'avons installé sous Debian :
$ sudo apt update
$ sudo apt install redis
Vous pouvez aussi opter pour ce site qui donne accès à une interface en ligne permettant de tester Redis et ses commandes. Elles sont d'ailleurs toutes détaillées dans cette page de la documentation. Il est aussi possible d'opter pour un conteneur Docker ou une solution hébergée chez des fournisseurs de service cloud (CSP).
Une fois l'installation effectuée, vérifiez que tout fonctionne :
$ redis-cli ping
Cela lance un serveur local (répondant sur l'interface 127.0.0.1 uniquement), lui envoie la commande PING et reçoit normalement un PONG. En cas de réussite, vous pouvez commencer à envoyer de premières commandes :
$ redis-cli set ceci_est_une_clé ceci_est_une_valeur
OK
$ redis-cli get ceci_est_une_clé
"ceci_est_une_valeur"
$ redis-cli del ceci_est_une_clé
(integer) 1
$ redis-cli get ceci_est_une_clé
(nil)
Ici on définit une valeur que l'on affiche, avant de la supprimer. En cas de réussite de l'opération le résultat est un simple « 1 ». Si vous cherchez à accéder à cette clé par la suite, elle renvoie alors la valeur nil
(la clé n'existe pas).
Notez que vous pouvez simplement lancer redis-cli
et enchaîner les commandes les unes derrière les autres. Bien d'autres fonctionnalités sont proposées, comme par exemple l'incrément de valeurs, entières ou non :
> set compteur 0
OK
> incr compteur
(integer) 1
> incrby compteur 9
(integer) 10
> 5 incr compteur
(integer) 11
(integer) 12
(integer) 13
(integer) 14
(integer) 15
> incrbyfloat compteur 0.1
"15.1"
> quit
Les valeurs de base étant considérées comme des Strings, vous pouvez les concaténer, obtenir leur longueur, agir sur une partie seulement ou même combiner certaines actions (GETDEL, GETEX, SETEX). La liste est là.
Invalider automatiquement une donnée
La commande SET peut également prendre des arguments pour préciser un délai d'expiration en secondes (EX) ou millisecondes (PX) ou sous la forme d'un temps UNIX (EXAT/PXAT). Sa documentation complète est ici.
Cette fonctionnalité peut être intéressante dans le cas d'un cache, pour déterminer si une donnée peut être obtenue via le serveur Redis depuis un temps plus ou moins long. Un nombre de visiteurs sur un site ne sera en effet pas géré de la même manière qu'une information « froide » comme la date de publication d'un élément.
Si une valeur n'est utile que pour un temps donné, vous pouvez donc la faire expirer :
> set temporaire va_disparaitre EX 1337
OK
> ttl temporaire
(integer) 1303
> expire temporaire 42
(integer) 1
> ttl temporaire
(integer) 36
> get temporaire
(nil)
Empreintes, (Sorted) Sets et Listes
Pour des structures plus complètes on peut utiliser les Empreintes (hash) qui permettent pour chaque clé de stocker plusieurs champs, chacun associé à une valeur. Imaginons par exemple une base d'utilisateurs :
> hset utilisateurs:1 pseudo toto
(integer) 1
> hset utilisateurs:1 age 42 pays Laponie
(integer 2)
> hget utilisateurs:1 age
"42"
> hgetall utilisateurs:1
1) "pseudo"
2) "toto"
3) "age"
4) "42"
5) "pays"
6) "Laponie"
Nous avons opté pour le nom de clé « utilisateurs:1 » mais cela ne répond à aucune convention particulière, vous pourriez très bien opter pour « utilisateurs-1 » si vous le préférez par exemple. C'est une manière d'organiser les clés comme une autre. On note également que lorsque l'on récupère l'ensemble des données, elles ne sont pas présentées sous la forme d'une association champ/valeur mais les unes à la suite des autres.
Les Empreintes peuvent faire, comme les clés classiques, l'objet d'un incrément d'entier/flottant, on peut demander uniquement la liste des clés, à connaître le nombre d'éléments, etc. Là aussi la documentation détaille tout.
Si vous cherchez simplement à regrouper différentes informations, les Sets peuvent vous aider, prenant la forme d'une clé associée à des valeurs non ordonnées que l'on peut ajouter, retirer, extraire, comparer, déplacer, etc.
> sadd courses "jus de fruit" bacon cheddar
(integer) 3
> smembers courses
1) "cheddar"
2) "jus de fruit"
3) "bacon"
> spop courses 2
1) "jus de fruit"
2) "cheddar"
> smembers courses
1) "bacon"
> sadd courses:old "jus de fruit" bacon cheddar
(integer) 3
> sdiff courses:old courses
1) "jus de fruit"
2) "cheddar"
> smove courses:old courses cheddar
(integer) 1
> smembers courses
1) "cheddar"
2) "bacon"
Il en existe une version ordonnée (Sorted Sets) qui se distingue par un « Z » en début de commande. Chaque valeur est associée à un « score » qui permet de le « ranger » :
> zadd classement 5 Sébastien
(integer 1)
> zadd classement 1 David 150 Harou
(integer 2)
> zrange classement 0 3
1) "David"
2) "S\xc3\xa9bastien"
3) "Harou"
> zrange classement 0 3 withscores
1) "David"
2) "1"
3) "S\xc3\xa9bastien"
4) "5"
5) "Harou"
6) "150"
Outre cette possibilité de classement, ce type de données peut être intéressant par les nombreuses fonctions qu'il propose pour comparer différents Sorted Sets, faire des associations, des croisements, etc.
Les Listes, elles, sont ordonnées par ordre d'insertion et ont une différence avec les Sets : il peut y avoir plusieurs itérations d'un même élément associé à une clé. On les pousse ou retire, mais on peut également le faire à différentes positions dans la liste, les déplacer, les gérer par leur index, en connaître la longueur, etc.
> lpush todo "Fouetter Séb" "Trouver de la DDR5" "Finir ce papier sur IPv6"
(integer) 3
> LLEN todo
(integer) 3
> lrange todo 0 3
1) "Finir ce papier sur IPv6"
2) "Trouver de la DDR5"
3) "Fouetter S\xc3\xa9b"
> lpush todo "Me faire un café"
(integer 4)
> rpush todo "Lire mes e-mails"
(integer 5)
> lrange todo 0 5
1) "Me faire un caf\xc3\xa9"
2) "Finir ce papier sur IPv6"
3) "Trouver de la DDR5"
4) "Fouetter S\xc3\xa9b"
5) "Lire mes e-mails"
> rpop todo
"Lire mes e-mails"
> lrange todo 0 4
1) "Me faire un caf\xc3\xa9"
2) "Finir ce papier sur IPv6"
3) "Trouver de la DDR5"
4) "Fouetter S\xc3\xa9b"
> ltrim todo 2 3
OK
> lrange todo 0 2
1) "Trouver de la DDR5"
2) "Fouetter S\xc3\xa9b"
> linsert todo after "Trouver de la DDR5" "Finir ce papier sur IPv6"
(integer 3)
> linsert todo after "Trouver de la DDR5" "Finir ce papier sur IPv6"
(integer 4)
> lrange todo 0 3
1) "Trouver de la DDR5"
2) "Finir ce papier sur IPv6"
3) "Finir ce papier sur IPv6"
4) "Fouetter S\xc3\xa9b"
PubSub : échangez des messages
Redis implémente une fonction de messagerie de type « Publish Subscribe » permettant donc de s'abonner à des canaux pour recevoir des informations par leur biais lorsque des messages sont publiés :
> subscribe canal:général canal:privé
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "canal:g\xc3\xa9n\xc3\xa9ral"
3) (integer) 1
1) "subscribe"
2) "canal:priv\xc3\xa9"
3) (integer) 2
Si un message est envoyé avec cette commande par exemple :
$ redis-cli publish canal:général "Coucou"
Un nouveau message s'affichera :
1) "message"
2) "canal:g\xc3\xa9n\xc3\xa9ral"
3) "Coucou"
Vous pouvez aussi obtenir des informations sur les canaux :
$ redis-cli pubsub channels
1) "canal:priv\xc3\xa9"
2) "canal:g\xc3\xa9n\xc3\xa9ral"
$ redis-cli pubsub numsub canal:général
1) "canal:g\xc3\xa9n\xc3\xa9ral"
2) (integer) 1
Groupez vos commandes
Redis permet de créer une transaction de plusieurs commandes, exécutées en une fois. Il est aussi possible d'utiliser un fichier contenant une liste de commandes à exécuter, pour un import de données par exemple :
> multi
OK
> set compteur 0
QUEUD
> 5 incr compteur
QUEUED
QUEUED
QUEUED
QUEUED
QUEUED
> incrbyfloat compteur 0.1
QUEUED
> exec
exec
1) OK
2) (integer) 1
3) (integer) 2
4) (integer) 3
5) (integer) 4
6) (integer) 5
7) "5.1"
Notez que dans le cas d'un cluster où plusieurs instances peuvent accéder aux données, il est possible d'utiliser la commande WATCH pour indiquer qu'une transaction ne peut être exécutée que si certaines valeurs n'ont pas été modifiées. Si c'est le cas, la transaction complète est alors abandonnée.
Faisons désormais la même opération, mais depuis un fichier commands.txt
contenant les commandes :
set compteur 0
5 incr compteur
incrbyfloat compteur 0.1
On peut l'exécuter sous la forme d'un pipe (plusieurs commandes groupées), avec ou sans timeout (en secondes). Cela renverra une erreur parce que la seconde ligne n'est pas reconnue, l'argument -r
étant nécessaire avec redis-cli
. Il faut donc plutôt placer cinq fois la commande ou exécuter un incrément de 5 directement.
On obtient alors le résultat suivant :
$ cat commands.txt | redis-cli --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 7
$ cat commands.txt | redis-cli --pipe-timeout 10
OK
(integer) 1
(integer) 2
(integer) 3
(integer) 4
(integer) 5
"5.1"
Accès aux fichiers
Redis permet de stocker des données plus conséquentes comme des fichiers, par exemple une image. Il faut pour cela utiliser le paramètre -x qui indique que l'on va transmettre des données depuis stdin:
$ redis-cli -x set images:ciel < ciel.jpg
Pour effectuer l'étape inverse on utilisera le paramètre --raw
qui permet d'obtenir une sortie brute :
$ redis-cli --raw get images:ciel > sortie.jpg
Cela peut aussi être utile pour lire le contenu d'un fichier texte stocké en mémoire via Redis par exemple :
$ echo "Hello, Word !" > hello.txt
$ redis-cli -x set texte < hello.txt
OK
$ redis-cli --raw get texte
"Hello, World !
Statistiques, sauvegarde et persistance
À l'inverse, on peut obtenir des informations sur les données stockées par le serveur et les sauvegarder :
$ redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
2178179 12.19G 1 0 94 (+0) 39
Affichera en temps réel l'évolution de la quantité de mémoire utilisée par le serveur, le nombre de clients (bloqués ou non), de requêtes et de connexions. Vous pouvez d'ailleurs surveiller les commandes en temps réel :
$ redis-cli monitor
Ou demander des informations plus particulières :
$ redis-cli --scan
$ redis-cli --scan | head -10
Affichera l'ensemble des clés ou les 10 premières
$ redis-cli --bigkeys
Affichera des statistiques sur les plus gros éléments de la base.
À tout moment, vous pouvez effectuer une sauvegarde de l'ensemble des données dans un fichier au format .rdb
:
$ redis-cli --rdb backup-$(date +%s).rdb
Nous utilisons ici la fonction date
pour obtenir le timestamp du moment de la sauvegarde et l'utiliser dans le nom du fichier. Vous pouvez la faire localement, mais aussi en montant un dossier distant ou un espace S3 par exemple. Notez que Redis propose des fonctionnalités de persistance au-delà de RoF. Outre la sauvegarde dans une Redis Database (RDB) comme évoquée ci-dessus, il y a l'Append Only File (AOF) et les instantanés, détaillés ici.
Lancer un serveur Redis accessible depuis un client tiers
Jusqu'à maintenant, nous avons utilisé un serveur Redis local, mais l'objectif est en général d'y donner accès depuis une machine distante et pourquoi pas de cumuler plusieurs serveurs entre eux au sein d'un cluster.
La première chose à faire est de déterminer l'interface réseau (et donc l'adresse IP) et le port depuis lesquels vous voulez que votre serveur soit accessible. Vous pouvez le lancer avec ou sans mot de passe exigé. Cela peut passer par un fichier de configuration ou une simple ligne de commande. Et être automatisé au démarrage de Linux.
Commencez par vérifier qu'un serveur n'est pas déjà lancé :
$ redis-cli-shutdown
Si vous utilisez Systemd (comme c'est le cas sous Debian), un service Redis peut-être présent et actif :
$ systemctl status redis
Vous pouvez éditer son fichier de configuration :
$ sudo nano /etc/redis/redis.conf
Il contient toute la documentation nécessaire pour utiliser ce fichier et ses différents paramètres. Désactivez ce service s'il n'est pas nécessaire ou que vous voulez procéder autrement :
$ systemctl stop redis
$ systemctl disable redis
Vous pouvez créer un fichier de configuration et restreindre son accès (puisqu'il contient un mot de passe) :
$ sudo touch /etc/redis-6380.conf
$ sudo chmod 700 /etc/redis-6380.conf
$ sudo nano /etc/redis-6380.conf
Placez-y les paramètres suivants (à adapter à vos besoins)
bind 192.168.0.42
port 6380
dir /var/lib/redis
protected-mode yes
requirepass votre_mot_de_passe
maxmemory 1gb
rdbcompression yes
rdbchecksum yes
loglevel notice
logfile /var/log/redis/redis-server.log
tcp-backlog 511
timeout 0
tcp-keepalive 300
Notez que le paramètre bind peut prendre plusieurs adresses IP si vous disposez de plusieurs interfaces.
Pour lancer le serveur il suffit ensuite de taper la commande suivante :
$ sudo redis-server /etc/redis/redis-6380.conf
Pour vous y connecter depuis une machine de votre réseau local, tapez :
$ redis-cli -h 192.168.0.42 -p 6380
Une fois connectez, identifiez-vous :
> auth votre_mot_de_passe
OK
Vous pouvez créer plusieurs serveurs au sein d'une même machine en utilisant différents ports et aller plus loin en créant des clusters d'instances, un script étant prévu à cet effet. Mais aussi à travers la réplication qui permet d'avoir une instance maître dont les données sont copiées sur d'autres, esclaves.
Performances et latence
Si vous voulez connaître les performances de votre serveur Redis, local ou distant, vous avez plusieurs moyens à votre disposition. Tout d'abord pour mesurer la latence à différents niveaux :
$ redis-cli --latency
$ redis-cli --latency --raw
Dans le premier cas, vous obtiendrez une mesure de latence en continu, avec un minimum, un maximum et une moyenne. La seconde commande effectue le test 1 seconde et affiche un résultat. --csv
est aussi utilisable.
$ redis-cli --latency-history
$ redis-cli --latency-history -i 1
Cette fois on effectue un relevé continu, mais avec un relevé régulier. Toutes les 15 secondes par défaut mais on peut modifier cette valeur avec le paramètre -i
.
$ redis-cli --latency-dist -i 5
Affiche le résultat sous la forme d'un spectre coloré, toutes les 5 secondes (1 seconde par défaut).
$ redis-cli --intrinsic-latency 5
Cette commande, à exécuter uniquement depuis le serveur Redis, puisqu'elle permet d'isoler la latence qui est propre au système et son scheduleur ou à l'hyperviseur en cas de virtualisation.
Un outil de mesure de performances permet de connaître le nombre de requêtes pouvant être encaissées par le système avec de nombreux paramètres pouvant être modifiés : types de requêtes, nombre, taille, etc.
$ redis-benchmark -t get,set -n 1000000 -q
Pour utiliser les pipes avec jusqu'à 32 commandes groupées, des paquets de 100 ko et 500 connexions :
$ redis-benchmark -t get,set -n 1000000 -q -P 32 -d 100000 -c 500
Des interfaces graphiques pour gérer vos serveurs Redis
Notez enfin qu'outre le client redis-cli, bien d'autres outils permettent de gérer un serveur Redis, notamment de manière graphique. C'est par exemple le cas de Redis Insight, mais aussi de nombreux outils open source. Des services en ligne comme redsmin proposent un service similaire, avec un modèle freemium.
