Redis
 sql >> Base de données >  >> NoSQL >> Redis

Script de suppression de caractères génériques Redis utilisant les retours EVAL, SCAN et DEL Les commandes d'écriture ne sont pas autorisées après des commandes non déterministes

MISE À JOUR : ce qui suit s'applique aux versions Redis jusqu'à 3.2. À partir de cette version, la réplication basée sur les effets lève l'interdiction du non-déterminisme afin que tous les paris soient ouverts (ou plutôt activés).

Vous ne pouvez pas (et ne devriez pas) mélanger le SCAN famille de commandes avec n'importe quelle commande d'écriture dans un script, car la réponse de la première dépend des structures de données internes de Redis qui, à leur tour, sont uniques au processus serveur. En d'autres termes, deux processus Redis (par exemple, maître et esclave) ne sont pas garantis de renvoyer les mêmes réponses (donc dans le contexte de réplication Redis [qui n'est pas basé sur des opérations mais sur des instructions] qui le casserait).

Redis essaie de se protéger contre de tels cas en bloquant toute commande d'écriture (telle que DEL ) s'il est exécuté après une commande aléatoire (par exemple SCAN mais aussi TIME , SRANDMEMBER et similaires). Je suis sûr qu'il existe des moyens de contourner cela, mais voudriez-vous le faire ? N'oubliez pas que vous irez en territoire inconnu où le comportement du système n'est pas défini.

Au lieu de cela, acceptez le fait que vous ne devriez pas mélanger les lectures et les écritures aléatoires et essayez de penser à une approche différente pour résoudre votre problème, à savoir supprimer un trousseau de clés selon un modèle de manière atomique.

Demandez-vous d'abord si vous pouvez assouplir l'une des exigences. Doit-il être atomique ? Atomicité signifie que Redis sera bloqué pendant la durée de la suppression (quelle que soit l'implémentation finale) et que la durée de l'opération dépend de la taille de la tâche (c'est-à-dire du nombre de clés supprimées et de leur contenu [la suppression d'un grand ensemble est plus cher que de supprimer une chaîne courte par exemple]).

Si l'atomicité n'est pas indispensable, périodiquement/paresseusement SCAN et supprimer par petits lots. Si c'est un must, comprenez que vous essayez essentiellement d'imiter les diaboliques KEYS commande :) Mais vous pouvez faire mieux si vous avez une connaissance préalable du modèle.

En supposant que le modèle est connu pendant l'exécution de votre application, vous pouvez collecter les clés pertinentes (par exemple, dans un ensemble), puis utiliser cette collection pour actualiser la suppression d'une manière atomique et sécurisée pour la réplication, ce qui est plus efficace que de parcourir l'intégralité de l'espace de clés. .

Cependant, le problème le plus "difficile" est si vous devez exécuter une correspondance de modèle ad hoc tout en garantissant l'atomicité. Si tel est le cas, le problème se résume à obtenir un instantané filtré par modèle de l'espace de clés immédiatement suivi d'une succession de suppressions (en soulignant à nouveau :pendant que la base de données est bloquée). Dans ce cas, vous pouvez très bien utiliser KEYS dans votre script Lua et espérez le meilleur... (mais sachant très bien que vous pouvez recourir à SHUTDOWN NOSAVE assez rapidement :P).

La dernière optimisation consiste à indexer l'espace de clés lui-même. Les deux SCAN et KEYS sont essentiellement des analyses de table complètes, alors que se passerait-il si nous devions indexer cette table ? Imaginez que vous gardiez un index sur les noms des clés qui peuvent être interrogés lors d'une transaction - vous pouvez probablement utiliser un ensemble trié et des plages lexicographiques (HT @TwBert ) pour éliminer la plupart des besoins de correspondance de modèles. Mais à un coût important ... non seulement vous ferez une double comptabilité (stocker les coûts du nom de chaque clé dans la RAM et le processeur), mais vous serez obligé d'ajouter de la complexité à votre application. Pourquoi ajouter de la complexité ? Parce que pour implémenter un tel index, vous devez le maintenir vous-même dans la couche application (et éventuellement tous vos autres scripts Lua), en enveloppant soigneusement chaque opération d'écriture sur Redis dans une transaction qui met également à jour l'index.

En supposant que vous ayez fait tout cela (et en tenant compte des pièges évidents tels que le potentiel de bogues de la complexité supplémentaire, la charge d'écriture au moins doublée sur Redis, la RAM et le processeur, les restrictions de mise à l'échelle, etc.), vous pouvez vous féliciter. épaule et félicitez-vous d'avoir utilisé Redis d'une manière pour laquelle il n'a pas été conçu. Bien que les prochaines versions de Redis puissent (ou non) inclure de meilleures solutions pour ce défi (@TwBert - voulez-vous faire un RCP/contrib conjoint et encore pirater un peu Redis ? ), avant d'essayer cela, je vous invite vraiment à repenser les exigences d'origine et à vérifier que vous utilisez correctement Redis (c'est-à-dire en concevant votre "schéma" en fonction de vos besoins d'accès aux données).