Qu'est-ce qu'une image bitmap ?
Dans un tableau de bits, chaque bit individuel d'un tableau de bits (0 ou 1) est utilisé pour représenter un état. Cela permet de représenter un grand nombre d'états dans un espace extrêmement réduit (ce qui vaut vraiment la peine d'être exploré lorsque l'espace est limité et qu'il y a un grand nombre d'états à représenter).
Pour Redis Bitmaps, Redis utilise des chaînes pour stocker ces données binaires et fournit un ensemble de commandes qui traitent ces chaînes binaires comme des bitmaps. Ces commandes permettent de définir et d'obtenir n'importe quel bit dans une chaîne Redis avec une complexité temporelle constante (une autre raison intéressante d'explorer les cas d'utilisation potentiels suivants).
Premier cas d'utilisation : dans un système asynchrone
Supposons tout d'abord qu'il existe une tâche asynchrone composée d'un nombre fixe d'étapes. Chaque étape est traitée en parallèle et vous devez savoir quand chaque étape se termine pour déterminer quand la tâche est "terminée".
Avec un Bitmap Redis, dont la clé est la chaîne task id, vous pouvez suivre l'état de chaque étape en retournant un bit (de 0 à 1) au niveau du décalage correspondant à chaque numéro d'étape lorsque l'étape est terminée. Si le bit où le décalage est égal au nombre total d'étapes de la tâche est basculé en premier, alors la tâche dans son ensemble est terminée lorsque tous les bits avant ce bit de décalage de fin ont également été basculés.
Par exemple, si une tâche comporte 4 étapes, l'inversion du bit du décalage 4 (de 0 à 1) entraîne l'état initial suivant : 00001000 (dans cet exemple, les décalages et les nombres d'étapes sont basés sur zéro)
Maintenant, lorsque l'étape numéro 2 est terminée, l'état ressemble à ceci : 00101000
Lorsque les étapes 0 et 3 sont terminées, l'état ressemble à ceci : 10111000
Enfin, lorsque l'étape 1 est terminée, l'état ressemble à ceci : 11111000 (et parce que tous les bits avant le bit de fin au décalage 4 ont été retournés - la tâche est terminée)
Voyons maintenant l'exemple de projet Java que j'ai mentionné (https://github.com/burkaa01/redis-bitmap). Pour les deux cas d'utilisation, tout commence dans l'élémenttrigger-app. Dans l'applicationtrigger-appil y a untriggerDone(et une définition de l'API) dansTriggerController.javaqui déclenchera une tâche d'exemple. La méthodecountreprésente le nombre d'étapes de la tâche d'exemple déclenchée.
La première fois que le projet interagit avec le bitmap Redis (via le client Jedis), c'est dans cette tâchetriggerDone. C'est là que le décalage de bit égal à la valeur du paramètre "count" est calculé. C'est là que le bit offset correspondant au nombre total d'étapes de la tâche est inversé (comme décrit ci-dessus) :
// mettre le bit "last" à vrai, initialisant un marqueur de fin d'étapes pour cette tâche
// avant que cette tâche ne soit considérée comme terminée, tous les bits précédant ce "dernier" bit doivent également être mis à vrai
Jedis jedis = nouveau Jedis(redisConnectionInfo) ;
jedis.setbit(taskId, count, true) ;
Ensuite, la méthode émet des requêtes pour chaque étape individuelle en parallèle à l'API définie dansdone-app. Dans l'exemple d'état étape par étape ci-dessus, il y a un élément de logique important que j'ai passé sous silence : comment déterminer exactement que tous les bits avant notre bit de fin ont été retournés ?
Cette logique se trouve dans la fonctionisDonedans le fichierDoneController.javaet elle ressemble à ceci :
privatestaticboolean isDone(Jedis jedis, String task) {
// obtient l'indice du plus petit bit erroné à utiliser pour déterminer si la tâche est terminée
Long leftMostZero = jedis.bitpos(task, faux) ;
// compte le nombre de bits vrais, c'est-à-dire le nombre d'étapes effectuées
long count = jedis.bitcount(task) ;
// vérifie si la tâche est terminée
booléen
done = (leftMostZero ==
null
|| leftMostZero < 0 || leftMostZero == count) ;
si
(done) {
LOGGER.info("Done with all {} steps for task : {}", count - 1, task) ;
}
return
done ;
}
Trois éléments rendent cette détermination possible :
La commande Redis BITPOS qui renvoie la position du premier bit mis à 0
La commande Redis BITCOUNT qui compte le nombre de bits mis à 0.
Le fait que dans la commandetrigger-apple premier bit inversé est celui dont le décalage est égal au nombre total de pas.
Mettez ces trois éléments ensemble et vous saurez que si vous ne trouvez pas de BITPOS 0 ou si ce BITPOS 0 est égal au BITCOUNT, la tâche est terminée.
Jetons un dernier coup d'œil à cet exemple initial, étape par étape :
Dans l'état initial, BITPOS 0 est 0 et BITCOUNT est 1 (la tâche n'est pas terminée) : 00001000
Lorsque les étapes 0, 2 et 3 sont terminées, BITPOS 0 est 1 et BITCOUNT est 4 (la tâche n'est toujours pas terminée) : 10111000
Enfin, lorsque l'étape 1 est terminée, BITPOS 0 et BITCOUNT sont tous deux égaux à 5 (la tâche est terminée !): 11111000
Deuxième cas d'utilisation : collecte de données d'activité distinctes
Passons au deuxième cas d'utilisation : la collecte de mesures d'activité distinctes. Le premier cas d'utilisation décrivait comment retourner un bit à une position unique en fonction d'un identifiant (les bits étaient retournés à chaque étape) et la commande BITCOUNT était utilisée pour la fonctionisDonepour l'ensemble de la tâche. Ces deux commandes sont tout ce dont vous aurez besoin pour compter et suivre le nombre d'utilisateurs uniques dans ce cas d'utilisation.
Jetons un coup d'œil àDistinctController.javadu projet d'exemple dans l'applicationdistinct-app. Cet exemple est assez simple, notre API prend en compte l'intuserIdet nous définissons le bit correspondant comme suit :
// mettre le bit à true, ce qui tient compte de l'utilisateur distinct
jedis.setbit(REDIS_KEY, userId, true) ;
Et si le bit a déjà été inversé à ce moment-làuserId(c'est-à-dire que cet utilisateur n'est pas nouveau/unique), le compte enregistré ci-dessous reste le même :
// compter le nombre de bits vrais, c'est-à-dire le nombre d'utilisateurs distincts
long count = jedis.bitcount(REDIS_KEY) ;
LOGGER.info("{} utilisateurs distincts", count) ;
Tout comme dans le cas d'utilisation précédent, si vous voulez essayer ce cas d'utilisation par vous-même, regardez dans le fichierTriggerController.java. La fonctiontriggerDistinct(et la définition de l'API) déclenchera une quantité variable d'activité "userId" et la méthodedistinct-appsuivra le nombre d'utilisateurs uniques en conséquence.
En conclusion
Je tiens à souligner que les bitmaps Redis ne conviennent pas à tous les problèmes, mais je pense qu'il s'agit d'un outil intelligent à avoir dans sa boîte à outils lorsque le temps et l'espace (en particulier l'espace) sont réduits et que vous avez beaucoup d'états à suivre. J'espère que cet article, l'exemple de projet Java fourni (https://github.com/burkaa01/redis-bitmap) et quelques cas d'utilisation fictifs ont suffi à l'illustrer.
Vous avez une question sur ce sujet du Mardi techno ? N'hésitez pas à nous contacter.