Disponible depuis 2.8.0.
Complexité en temps : O(1) pour chaque appel. O(N) pour une itération complète, y compris suffisamment d’appels de commande pour que le curseur revienne à 0. N est le nombre d’éléments à l’intérieur de la collection.
La commande SCAN et les commandes étroitement liées SSCAN, HSCAN et ZSCAN sont utilisées afin d’itérer de manière incrémentielle sur une collection d’éléments.
- SCAN itère l’ensemble des clés dans la base de données Redis actuellement sélectionnée.
- SSCAN itère les éléments de types Sets.
- HSCAN itère les champs de types Hash et leurs valeurs associées.
- ZSCAN itère les éléments de types Sorted Set et leurs notes associées.
Puisque ces commandes permettent une itération incrémentale, ne renvoyant qu’un petit nombre d’éléments par appel, elles peuvent être utilisées en production sans l’inconvénient de commandes comme KEYS ou SMEMBERS qui peuvent bloquer le serveur pendant un long moment (voire plusieurs secondes) lorsqu’elles sont appelées contre de grandes collections de clés ou d’éléments.
Toutefois, alors que les commandes bloquantes comme SMEMBERS sont capables de fournir tous les éléments qui font partie d’un Set à un moment donné, La famille de commandes SCAN n’offre que des garanties limitées sur les éléments renvoyés puisque la collection que nous itérons de manière incrémentielle peut changer pendant le processus d’itération.
Notez que SCAN, SSCAN, HSCAN et ZSCAN fonctionnent tous de manière très similaire, cette documentation couvre donc les quatre commandes. Cependant, une différence évidente est que dans le cas de SSCAN, HSCAN et ZSCAN, le premier argument est le nom de la clé détenant la valeur Set, Hash ou Sorted Set. La commande SCAN n’a pas besoin d’argument de nom de clé car elle itère les clés dans la base de données actuelle, donc l’objet itéré est la base de données elle-même.
- *Utilisation de base de SCAN
- *Garanties SCAN
- *Nombre d’éléments retournés à chaque appel SCAN
- *L’option COUNT
- *L’option MATCH
- *L’option TYPE
- *Multiples itérations parallèles
- *Terminer les itérations au milieu
- *Appeler SCAN avec un curseur corrompu
- *Garantie de terminaison
- *Pourquoi SCAN peut renvoyer tous les éléments d’un type de données agrégé en un seul appel ?
- *Return value
- *History
- *Additional examples
*Utilisation de base de SCAN
SCAN est un itérateur basé sur un curseur. Cela signifie qu’à chaque appel de la commande, le serveur renvoie un curseur mis à jour que l’utilisateur doit utiliser comme argument de curseur dans l’appel suivant.
Une itération commence lorsque le curseur est défini à 0, et se termine lorsque le curseur renvoyé par le serveur est 0. Voici un exemple d’itération SCAN :
redis 127.0.0.1:6379> scan 0 1) "17" 2) 1) "key:12" 2) "key:8" 3) "key:4" 4) "key:14" 5) "key:16" 6) "key:17" 7) "key:15" 8) "key:10" 9) "key:3" 10) "key:7" 11) "key:1" redis 127.0.0.1:6379> scan 17 1) "0" 2) 1) "key:5" 2) "key:18" 3) "key:0" 4) "key:2" 5) "key:19" 6) "key:13" 7) "key:6" 8) "key:9" 9) "key:11"
Dans l’exemple ci-dessus, le premier appel utilise zéro comme curseur, pour commencer l’itération. Le deuxième appel utilise le curseur renvoyé par l’appel précédent comme premier élément de la réponse, c’est-à-dire 17.
Comme vous pouvez le voir, la valeur de retour de SCAN est un tableau de deux valeurs : la première valeur est le nouveau curseur à utiliser dans l’appel suivant, la deuxième valeur est un tableau d’éléments.
Puisque dans le deuxième appel le curseur renvoyé est 0, le serveur a signalé à l’appelant que l’itération s’est terminée, et que la collection a été complètement explorée. Commencer une itération avec une valeur de curseur de 0, et appeler SCAN jusqu’à ce que le curseur retourné soit à nouveau 0 est appelé une itération complète.
*Garanties SCAN
La commande SCAN, et les autres commandes de la famille SCAN, sont capables de fournir à l’utilisateur un ensemble de garanties associées aux itérations complètes.
- Une itération complète récupère toujours tous les éléments qui étaient présents dans la collection du début à la fin d’une itération complète. Cela signifie que si un élément donné se trouve à l’intérieur de la collection lorsqu’une itération est lancée, et qu’il est toujours là lorsqu’une itération se termine, alors à un moment donné SCAN l’a renvoyé à l’utilisateur.
- Une itération complète ne renvoie jamais aucun élément qui n’était PAS présent dans la collection du début à la fin d’une itération complète. Ainsi, si un élément a été retiré avant le début d’une itération, et n’est jamais ajouté à nouveau à la collection pendant tout le temps que dure une itération, SCAN s’assure que cet élément ne sera jamais renvoyé.
Cependant, comme SCAN a très peu d’état associé (juste le curseur), il présente les inconvénients suivants :
- Un élément donné peut être renvoyé plusieurs fois. C’est à l’application de gérer le cas des éléments dupliqués, par exemple en n’utilisant les éléments retournés que pour effectuer des opérations qui sont sûres lorsqu’elles sont réappliquées plusieurs fois.
- Les éléments qui n’étaient pas constamment présents dans la collection lors d’une itération complète, peuvent être retournés ou non : ce n’est pas défini.
*Nombre d’éléments retournés à chaque appel SCAN
Les fonctions de la famille SCAN ne garantissent pas que le nombre d’éléments retournés par appel soient dans une fourchette donnée. Les commandes sont également autorisées à renvoyer des éléments nuls, et le client ne doit pas considérer que l’itération est terminée tant que le curseur renvoyé n’est pas nul.
Cependant, le nombre d’éléments renvoyés est raisonnable, c’est-à-dire qu’en termes pratiques, SCAN peut renvoyer un nombre maximal d’éléments de l’ordre de quelques dizaines d’éléments lors de l’itération d’une grande collection, ou peut renvoyer tous les éléments de la collection en un seul appel lorsque la collection itérée est suffisamment petite pour être représentée en interne comme une structure de données codée (cela se produit pour les petits ensembles, les hachages et les ensembles triés).
Cependant, il existe un moyen pour l’utilisateur d’accorder l’ordre de grandeur du nombre d’éléments retournés par appel en utilisant l’option COUNT.
*L’option COUNT
Bien que SCAN ne fournisse pas de garanties sur le nombre d’éléments retournés à chaque itération, il est possible d’ajuster empiriquement le comportement de SCAN en utilisant l’option COUNT. Fondamentalement, avec COUNT, l’utilisateur a spécifié la quantité de travail qui devrait être effectuée à chaque appel afin de récupérer les éléments de la collection. Ce n’est qu’une indication pour l’implémentation, cependant, de manière générale, c’est ce que vous pourriez attendre la plupart du temps de l’implémentation.
- La valeur COUNT par défaut est 10.
- Lorsque l’on itère l’espace clé, ou un Set, Hash ou Sorted Set qui est assez grand pour être représenté par une table de hachage, en supposant qu’aucune option MATCH n’est utilisée, le serveur retournera généralement count ou un peu plus que count éléments par appel. Veuillez consulter la section pourquoi SCAN peut renvoyer tous les éléments à la fois plus loin dans ce document.
- Lorsque l’on itère des Sets codés comme des intsets (petits ensembles composés uniquement d’entiers), ou des Hashes et Sorted Sets codés comme des ziplists (petits hashes et ensembles composés de petites valeurs individuelles), généralement tous les éléments sont renvoyés lors du premier appel SCAN, quelle que soit la valeur COUNT.
Important : il n’est pas nécessaire d’utiliser la même valeur COUNT pour chaque itération. L’appelant est libre de modifier le comptage d’une itération à l’autre selon ses besoins, tant que le curseur passé lors de l’appel suivant est celui obtenu lors de l’appel précédent à la commande.
*L’option MATCH
Il est possible de n’itérer que les éléments correspondant à un motif de style glob donné, de manière similaire au comportement de la commande KEYS qui prend un motif comme seul argument.
Pour ce faire, il suffit d’ajouter les arguments MATCH <pattern>
à la fin de la commande SCAN (cela fonctionne avec toutes les commandes de la famille SCAN).
Voici un exemple d’itération à l’aide de MATCH:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood (integer) 6 redis 127.0.0.1:6379> sscan myset 0 match f* 1) "0" 2) 1) "foo" 2) "feelsgood" 3) "foobar" redis 127.0.0.1:6379>
Il est important de noter que le filtre MATCH est appliqué après la récupération des éléments dans la collection, juste avant de renvoyer les données au client. Cela signifie que si le motif correspond à très peu d’éléments à l’intérieur de la collection, SCAN retournera probablement aucun élément dans la plupart des itérations. Un exemple est présenté ci-dessous:
redis 127.0.0.1:6379> scan 0 MATCH *11* 1) "288" 2) 1) "key:911" redis 127.0.0.1:6379> scan 288 MATCH *11* 1) "224" 2) (empty list or set) redis 127.0.0.1:6379> scan 224 MATCH *11* 1) "80" 2) (empty list or set) redis 127.0.0.1:6379> scan 80 MATCH *11* 1) "176" 2) (empty list or set) redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000 1) "0" 2) 1) "key:611" 2) "key:711" 3) "key:118" 4) "key:117" 5) "key:311" 6) "key:112" 7) "key:111" 8) "key:110" 9) "key:113" 10) "key:211" 11) "key:411" 12) "key:115" 13) "key:116" 14) "key:114" 15) "key:119" 16) "key:811" 17) "key:511" 18) "key:11" redis 127.0.0.1:6379>
Comme vous pouvez le voir, la plupart des appels ont retourné zéro élément, mais le dernier appel où un COUNT de 1000 a été utilisé afin de forcer la commande à faire plus de balayage pour cette itération.
*L’option TYPE
Depuis la version 6.0, vous pouvez utiliser cette option pour demander à SCAN de ne retourner que les objets qui correspondent à un type
donné, ce qui vous permet d’itérer dans la base de données à la recherche de clés d’un type spécifique. L’option TYPE est uniquement disponible sur le SCAN de la base de données entière, pas sur le HSCAN ou le ZSCAN etc.
L’argument type
est le même nom de chaîne que la commande TYPE renvoie. Notez une bizarrerie où certains types Redis, tels que GeoHashes, HyperLogLogs, Bitmaps et Bitfields, peuvent être implémentés en interne en utilisant d’autres types Redis, tels qu’une chaîne ou un zset, et ne peuvent donc pas être distingués des autres clés de ce même type par SCAN. Par exemple, un ZSET et un GEOHASH:
redis 127.0.0.1:6379> GEOADD geokey 0 0 value (integer) 1 redis 127.0.0.1:6379> ZADD zkey 1000 value (integer) 1 redis 127.0.0.1:6379> TYPE geokey zset redis 127.0.0.1:6379> TYPE zkey zset redis 127.0.0.1:6379> SCAN 0 TYPE zset 1) "0" 2) 1) "geokey" 2) "zkey"
Il est important de noter que le filtre TYPE est également appliqué après que les éléments sont récupérés dans la base de données, de sorte que l’option ne réduit pas la quantité de travail que le serveur doit faire pour compléter une itération complète, et pour les types rares, vous pouvez recevoir aucun élément dans de nombreuses itérations.
*Multiples itérations parallèles
Il est possible pour un nombre infini de clients d’itérer la même collection en même temps, car l’état complet de l’itérateur est dans le curseur, qui est obtenu et renvoyé au client à chaque appel. Côté serveur, aucun état n’est pris du tout.
*Terminer les itérations au milieu
Puisqu’il n’y a pas d’état côté serveur, mais l’état complet est capturé par le curseur, l’appelant est libre de terminer une itération à mi-chemin sans le signaler au serveur d’une quelconque manière. Un nombre infini d’itérations peut être lancé et jamais terminé sans aucun problème.
*Appeler SCAN avec un curseur corrompu
Appeler SCAN avec un curseur cassé, négatif, hors de portée, ou autrement invalide, entraînera un comportement indéfini mais jamais un crash. Ce qui sera indéfini, c’est que les garanties sur les éléments retournés ne peuvent plus être assurées par l’implémentation de SCAN.
Les seuls curseurs valides à utiliser sont :
- La valeur de curseur de 0 lors du démarrage d’une itération.
- Le curseur retourné par l’appel précédent à SCAN afin de poursuivre l’itération.
*Garantie de terminaison
L’algorithme SCAN est garanti de se terminer seulement si la taille de la collection itérée reste bornée à une taille maximale donnée, sinon l’itération d’une collection qui croît toujours peut avoir pour résultat que SCAN ne termine jamais une itération complète.
Ceci est facile à voir intuitivement : si la collection grandit, il y a de plus en plus de travail à faire pour visiter tous les éléments possibles, et la capacité à terminer l’itération dépend du nombre d’appels à SCAN et de la valeur de son option COUNT par rapport au taux de croissance de la collection.
*Pourquoi SCAN peut renvoyer tous les éléments d’un type de données agrégé en un seul appel ?
Dans la documentation de l’option COUNT
, nous indiquons que parfois cette famille de commandes peut renvoyer tous les éléments d’un ensemble, d’un hachage ou d’un ensemble trié en une seule fois en un seul appel, indépendamment de la valeur de l’option COUNT
. La raison pour laquelle cela se produit est que l’itérateur basé sur le curseur peut être mis en œuvre, et est utile, uniquement lorsque le type de données agrégées que nous analysons est représenté comme une table de hachage. Cependant, Redis utilise une optimisation de la mémoire où les petits types de données agrégées, jusqu’à ce qu’ils atteignent une quantité donnée d’éléments ou une taille maximale donnée d’éléments individuels, sont représentés en utilisant un encodage compact à allocation unique. Lorsque c’est le cas, SCAN n’a pas de curseur significatif à retourner, et doit itérer toute la structure de données à la fois, donc le seul comportement sain qu’il a est de tout retourner dans un appel.
Cependant, une fois que les structures de données sont plus grandes et sont promues pour utiliser de véritables tables de hachage, la famille de commandes SCAN aura recours au comportement normal. Notez que puisque ce comportement spécial de retour de tous les éléments n’est vrai que pour les petits agrégats, il n’a pas d’effets sur la complexité ou la latence de la commande. Cependant, les limites exactes pour être converties en véritables tables de hachage sont configurables par l’utilisateur, donc le nombre maximum d’éléments que vous pouvez voir renvoyés en un seul appel dépend de la taille que pourrait avoir un type de données agrégées tout en utilisant la représentation empaquetée.
Notez également que ce comportement est spécifique à SSCAN, HSCAN et ZSCAN. SCAN itself never shows this behavior because the key space is always represented by hash tables.
*Return value
SCAN, SSCAN, HSCAN and ZSCAN return a two elements multi-bulk reply, where the first element is a string representing an unsigned 64 bit number (the cursor), and the second element is a multi-bulk with an array of elements.
- SCAN array of elements is a list of keys.
- SSCAN array of elements is a list of Set members.
- HSCAN array of elements contain two elements, a field and a value, for every returned element of the Hash.
- ZSCAN array of elements contain two elements, a member and its associated score, for every returned element of the sorted set.
*History
-
>= 6.0
: Supports the TYPE subcommand.
*Additional examples
Iteration of a Hash value.
redis 127.0.0.1:6379> hmset hash name Jack age 33 OK redis 127.0.0.1:6379> hscan hash 0 1) "0" 2) 1) "name" 2) "Jack" 3) "age" 4) "33"