1. Pourquoi utiliser Redis
Lors de l'utilisation de Redis dans un projet, l'auteur estime que les performances et la concurrence doivent être prises en compte. Bien sûr, Redis a également d'autres fonctions qui peuvent effectuer des verrous distribués et d'autres fonctions, mais s'il s'agit uniquement d'autres fonctions telles que les verrous distribués, il existe d'autres middlewares (tels que Zookpeer, etc.) qui peuvent être utilisés à la place, et Redis ne doit pas être utilisé.
Par conséquent, cette question reçoit principalement une réponse sous les deux angles de la performance et de la concurrence :
1 Performance
Comme le montre la figure ci-dessous, lorsque nous rencontrons le besoin d'exécuter, il faut un temps particulièrement long, et le résultat Lorsque le SQL ne change pas fréquemment, il est particulièrement adapté de mettre les résultats en cours d'exécution dans le cache. De cette manière, les requêtes ultérieures seront lues à partir du cache, afin de pouvoir répondre rapidement aux requêtes.
Digression : J'ai soudain envie de parler de la norme de réponse rapide - en fait, il n'y a pas de norme fixe pour ce temps de réponse en fonction de l'effet d'interaction. Quelqu'un me l'a dit un jour : "Idéalement, nos sauts de page devraient être effectués instantanément et les opérations dans la page doivent être effectuées en un instant.". De plus, les opérations fastidieuses qui nécessitent plus qu'un simple claquement de doigt doivent comporter des invites de progression et peuvent être suspendues ou annulées à tout moment, afin d'offrir aux utilisateurs la meilleure expérience possible. «
Alors, combien de temps représente un instant, un instant ou un simple mouvement du doigt ?
Selon les enregistrements du « Maha Sangha Vinaya » : un instant est une pensée, vingt pensées sont un instant, vingt instants sont un seul mouvement. du doigt, vingt coups de doigt sont Un Luo est un instant, vingt Luo est un instant, et un jour et une nuit sont trente instants
Donc, après un calcul minutieux, un instant équivaut à 0,36 seconde, un instant équivaut à 0,018. secondes, et un doigt dure 7,2 secondes
2. Concurrence
Lorsque le degré de concurrence est élevé, toutes les demandes d'accès direct à la base de données entraîneront des exceptions de connexion à la base de données, comme le montre la figure. évitez l'accès direct à la base de données, nous pouvons utiliser Redis pour la mise en mémoire tampon dans ce cas. Opération, laissez la requête accéder à Redis en premier 2. Quels sont les inconvénients de l'utilisation de Redis ?Tout le monde utilise Redis depuis si longtemps. Ce problème doit être compris. Fondamentalement, en utilisant Redis, tout le monde rencontrera des problèmes, les plus courants étant principalement de quatre aspects :
1. 2. Problème d'avalanche de cache
3. Problème de panne de cache 4. Questions de concurrence de cacheL'auteur estime personnellement que ces quatre problèmes sont plus courants dans les projets. Les solutions spécifiques seront données plus tard.
3. Pourquoi Redis monothread est-il si rapideCette question est en fait un examen du mécanisme interne de Redis. Dans l'expérience d'entretien de l'auteur, beaucoup de gens ne réalisent pas que Redis utilise un. Modèle de travail monothread. Par conséquent, cette question doit être revue principalement sur les trois points suivants :
1. Fonctionnement en mémoire pure
2. Mécanisme de multiplexage d'E/S
Parlons des E/S plus en détail Mécanisme de multiplexage, car ce terme est trop populaire pour que les gens ordinaires en comprennent la signification. Par exemple : Xiaoqu a ouvert un magasin de messagerie dans la ville S et était responsable de l'intra-. livraison express en ville. En raison de contraintes financières, Xiaoqu a d'abord embauché de nombreux coursiers, mais a découvert plus tard que ce n'est qu'en achetant une voiture que l'on peut avoir suffisamment de fonds pour effectuer la livraison express. Lors de la livraison, Xiaoqu laissera un coursier le surveiller, puis le coursier se rendra pour livrer la livraison express. Lentement, Xiaoqu a découvert qu'il y avait de nombreux problèmes avec cette méthode commerciale. Des dizaines de coursiers passaient essentiellement leur temps à récupérer des voitures. les coursiers étaient inactifs. Celui qui attrapait la voiture pouvait l'obtenir. Allez livrer en express Avec l'augmentation de la livraison express, il y a de plus en plus de coursiers Xiaoqu ont constaté que le magasin express est de plus en plus encombré et qu'il n'y a aucun moyen. pour embaucher de nouveaux coursiers. La coordination entre les coursiers prend beaucoup de temps. Sur la base des lacunes ci-dessus, Xiaoqu a tiré les leçons de l'expérience et a proposé la méthode commerciale suivante ↓
Méthode commerciale deux : . Xiaoqu n'a embauché qu'un seul coursier et a classé la livraison express du client en fonction de la destination, soigneusement placée au même endroit. Enfin, le coursier récupère les coursiers dans l'ordre, les prend un par un, chasse le coursier, puis. revient chercher le prochain coursier.En comparant les deux méthodes commerciales ci-dessus, est-il évident que la seconde est plus efficace et meilleure ? Dans la métaphore ci-dessus :
1. Chaque courrier → chaque fil
2. Chaque express → chaque Socket (flux E/S)
3. Le lieu de livraison de l'express → différents états du Socket
4. Celui du client. demande de livraison express → demande du client
5. La méthode commerciale de Xiaoqu → code exécuté sur le serveur
6. Une voiture → nombre de cœurs CPU
Nous avons donc les conclusions suivantes :
1. Fonctionnement La première méthode est le modèle de concurrence traditionnel. Chaque flux d'E/S (express) est géré par un nouveau thread (courier).
2. La deuxième méthode de gestion est le multiplexage des E/S. Un coursier gère plusieurs flux d'E/S en suivant l'état de chaque flux d'E/S. C'est similaire à un coursier qui n'a qu'une seule personne pour livrer chaque colis et a besoin de connaître l'état de livraison de chaque colis.
Ce qui suit est une analogie avec le véritable modèle de thread Redis, comme le montre la figure :
En vous référant à la figure ci-dessus, pour faire simple, lorsque notre client Redis fonctionnera, il générera des Sockets avec différents événements genres. Côté serveur, il existe un programme de multiplexage d'E/S qui le met dans une file d'attente. Ensuite, le répartiteur d'événements de fichier le extrait tour à tour de la file d'attente et le transmet à différents processeurs d'événements.
Il convient de noter que pour ce mécanisme de multiplexage d'E/S, Redis fournit également des bibliothèques de fonctions de multiplexage telles que Select, Epoll, Evport et Kqueue. Vous pouvez en apprendre davantage par vous-même.
4. Les types de données Redis et leurs scénarios d'utilisation respectifs
Quand vous voyez cette question, pensez-vous qu'elle est très basique ? En fait, je le pense aussi. Cependant, d'après l'expérience des entretiens, au moins 80 % des personnes ne peuvent pas répondre à cette question. Il est recommandé qu'après l'avoir utilisé dans le projet, vous puissiez le mémoriser par analogie pour acquérir une expérience plus profonde au lieu de le mémoriser par cœur. Fondamentalement, un programmeur qualifié utilisera cinq types :
1, String
Il n'y a en fait rien à dire à ce sujet. Pour l'opération Set/Get la plus courante, Value peut être soit une chaîne, soit un nombre. , effectue généralement une mise en cache de fonctions de comptage complexes.
2. Hash
La valeur ici est une variable contenant un objet structuré, qui permet une manipulation pratique de champs spécifiques. Lorsque l'auteur effectue une connexion unique, j'utilise cette structure de données pour stocker les informations utilisateur, j'utilise CookieId comme clé et je définis 30 minutes comme délai d'expiration du cache, ce qui peut très bien simuler un effet de type session.
3. List
En utilisant la structure de données de List, vous pouvez exécuter des fonctions simples de file d'attente de messages. De plus, vous pouvez également utiliser la commande Lrange de Redis pour implémenter la fonction de pagination, qui offre d'excellentes performances et peut offrir une bonne expérience utilisateur.
4. Set
Parce que Set empile un tas d'ensembles de valeurs uniques, il peut effectuer une déduplication globale.
Pourquoi ne pas utiliser le Set fourni avec la JVM pour la déduplication ? Nos systèmes étant généralement déployés en clusters, il est gênant d'utiliser le Set fourni avec la JVM. Est-il nécessaire de créer un service public à des fins de déduplication globale ? C'est trop de problèmes.
De plus, en utilisant l'intersection, l'union, la différence et d'autres opérations, vous pouvez calculer les préférences communes, toutes les préférences, vos propres préférences uniques et d'autres fonctions.
5. Ensemble trié
En attribuant le paramètre Score aux éléments de l'ensemble, l'ensemble trié peut trier les éléments en fonction du score. Vous pouvez créer une demande de classement et effectuer des opérations TOP N. De plus, Sorted Set peut également être utilisé pour effectuer des tâches différées. La dernière application consiste à effectuer des recherches par plage.
5. Stratégie d'expiration de Redis et mécanisme d'élimination de la mémoire
L'importance de cette question va de soi, elle peut révéler si Redis a été appliqué correctement. Par exemple, si votre Redis ne peut stocker que 5 Go de données et que vous écrivez 10 Go, 5 Go de données seront supprimés. Comment a-t-il été supprimé ? Avez-vous réfléchi à ce problème ? De plus, vos données ont défini un délai d'expiration, mais une fois le délai écoulé, l'utilisation de la mémoire est encore relativement élevée. Avez-vous réfléchi à la raison ?
Redis adopte une stratégie de suppression régulière + suppression paresseuse.
Pourquoi ne pas utiliser une stratégie de suppression programmée ?
Suppression régulière, utilisez une minuterie pour surveiller la clé, et elle sera automatiquement supprimée à son expiration. Bien que la mémoire soit libérée avec le temps, elle consomme beaucoup de ressources CPU. En cas de requêtes simultanées volumineuses, le processeur doit utiliser du temps pour traiter la requête au lieu de supprimer la clé, cette stratégie n'est donc pas adoptée.
Comment fonctionnent la suppression régulière + la suppression différée ?
Suppression régulière, Redis vérifie par défaut s'il y a des clés expirées toutes les 100 ms, et s'il y a des clés expirées, supprimez-les. Il convient de noter que Redis ne vérifie pas toutes les clés toutes les 100 ms, mais les sélectionne et les vérifie de manière aléatoire (si toutes les clés sont vérifiées toutes les 100 ms, Redis ne sera pas bloqué). Par conséquent, si vous adoptez uniquement une stratégie de suppression régulière, de nombreuses clés ne seront pas supprimées à la fin des temps.
Donc, la suppression paresseuse est pratique. C'est-à-dire que lorsque vous obtenez une clé, Redis vérifiera si le délai d'expiration est défini pour cette clé, a-t-elle expiré ? S'il expire, il sera supprimé à ce moment-là.
N'y a-t-il pas d'autres problèmes si nous adoptons la suppression régulière + la suppression paresseuse ?
Non, si la clé n'est pas supprimée par suppression régulière. Ensuite, vous n'avez pas demandé la clé à temps, ce qui signifie que la suppression différée n'a pas pris effet. Afin d'éviter que la mémoire de Redis n'augmente continuellement, le mécanisme d'élimination de la mémoire doit être activé.
Il y a une ligne de configuration dans Redis.conf :
# maxmemory-policy volatile-lru
Cette configuration est équipée de la stratégie d'élimination de mémoire :
Noeviction : Quand la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, les nouvelles opérations d'écriture signaleront une erreur. Personne ne devrait l'utiliser ;
Allkeys-lru : Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, dans l'espace clé, supprimez la clé la moins récemment utilisée. Recommandé, actuellement le projet utilise ceci ;
Allkeys-random : Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, dans l'espace clé, une clé est supprimée au hasard et personne ne doit l'utiliser ;
Volatile-lru :Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, dans l'espace clé avec un délai d'expiration défini, supprimez la clé la moins récemment utilisée. Cette situation est généralement utilisée lorsque Redis est utilisé à la fois comme cache et comme stockage persistant. Non recommandé ;
Volatile-aléatoire :Lorsque la mémoire est insuffisante pour accueillir les données nouvellement écrites, une clé est supprimée de manière aléatoire dans l'espace clé avec un délai d'expiration défini. Toujours pas recommandé ;
Volatile-ttl :Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, dans l'espace clé avec un délai d'expiration défini, la clé avec un délai d'expiration antérieur sera supprimée en premier. Non recommandé. PS : Si la clé d'expiration n'est pas définie et que les conditions préalables ne sont pas remplies, alors le comportement des stratégies Volatile-lru, Volatile-random et Volatile-ttl est fondamentalement le même que celui de Noeviction (pas de suppression).
6. Problème de cohérence en double écriture entre Redis et la base de données
Le problème de cohérence est un problème distribué courant, qui peut être divisé en cohérence finale et cohérence forte. Si la base de données et le cache sont écrits en double, il y aura inévitablement des incohérences. Pour répondre à cette question, nous devons d'abord comprendre une prémisse : s'il existe de fortes exigences de cohérence pour les données, le cache ne peut pas être placé. Tout ce que nous faisons ne peut que garantir une cohérence à terme.
La solution que nous proposons ne peut que réduire la possibilité d'incohérence, mais ne peut pas l'éliminer complètement. Par conséquent, les données présentant de fortes exigences de cohérence ne peuvent pas être mises en cache.
"Solution de cohérence de double écriture de base de données distribuée et de cache"
donne une analyse détaillée Voici une brève explication : Tout d'abord, adoptez la bonne stratégie de mise à jour, mettez d'abord à jour la base de données, puis supprimez le cache dans un deuxième temps, car il peut y avoir des problèmes. Il peut s'agir d'un problème d'échec de suppression du cache. Fournissez simplement une mesure de compensation, telle que l'utilisation d'une file d'attente de messages.
7. Gérer les problèmes de pénétration du cache et d'avalanche de cache
En général, les petites et moyennes entreprises de logiciels traditionnelles rencontrent rarement des problèmes de pénétration du cache et d'avalanche de cache. Si vous souhaitez gérer des millions de trafic, ces deux problèmes doivent être soigneusement examinés :
1 Gérer la pénétration du cache
La pénétration du cache, c'est-à-dire que les pirates informatiques demandent délibérément des données qui n'existent pas dans les données du cache, provoquant l'envoi de toutes les requêtes à la base de données, provoquant une connexion anormale à la base de données.
Solution :
Utilisez un verrou mutex. Lorsque le cache échoue, obtenez d'abord le verrou, puis demandez la base de données. Si vous n'obtenez pas le verrou, dormez un moment et réessayez ;
1. Utilisez une stratégie de mise à jour asynchrone, que Key obtienne ou non la valeur, elle sera renvoyée directement. Un délai d'expiration du cache est maintenu dans la valeur Value. Si le cache expire, un thread sera démarré de manière asynchrone pour lire la base de données et mettre à jour le cache. Une opération de préchauffage du cache est requise (charger le cache avant de démarrer le projet) ;2. Fournir une méthode permettant de déterminer rapidement la demande s'il existe un mécanisme d'interception efficace, tel que l'utilisation d'un filtre Bloom pour maintenir en interne une série de clés légales et valides, et déterminer rapidement si la clé transportée dans la demande est légale et valable, et s'il n'est pas légal, il sera restitué directement.
2. Faire face à l'avalanche de cache Avalanche de cache, c'est-à-dire que le cache échoue dans une grande zone en même temps, une autre vague de demandes arrive, et par conséquent, la. les requêtes sont toutes envoyées à la base de données, provoquant des exceptions de connexion à la base de données. Solution :1. Ajoutez une valeur aléatoire au délai d'expiration du cache pour éviter un échec collectif ;
2 Utilisez un verrou mutex, mais le débit de cette solution a considérablement diminué ; Nous avons deux caches, le cache A et le cache B. Le délai d'expiration du cache A est de 20 minutes et il n'y a pas de délai d'expiration pour le cache B. L'opération de préchauffage du cache est effectuée par elle-même.
Décomposez ensuite les points suivants :
a. Lisez la base de données à partir du cache A, et revenez directement s'il y en a ;
b Si A n'a pas de données, lisez les données directement depuis B, retournez directement, et démarrer un thread de mise à jour de manière asynchrone ;c. Le thread de mise à jour met à jour le cache A et le cache B en même temps.Ce problème est à peu près que plusieurs sous-systèmes définissent une clé en même temps. Dans ce cas, il faut veiller à utiliser le mécanisme de transaction Redis. D’après mes résultats de recherche préalables sur Baidu, la plupart des gens recommandent cette méthode. Mais je ne recommande pas d’utiliser le mécanisme de transaction de Redis. Nous utilisons principalement des clusters Redis dans l'environnement de production et effectuons le partage de données. Lorsque vous impliquez plusieurs opérations de clé dans une transaction, ces clés ne sont pas nécessairement stockées sur le même serveur Redis. Par conséquent, le mécanisme de transaction de Redis est très inutile.
La solution est la suivante : Si l'ordre de cette opération de clé n'est pas requisDans ce cas, préparez une serrure distribuée, tout le monde saisira la serrure, et effectuera simplement l'opération Set après avoir saisi la serrure. C'est relativement simple.
Si la séquence est requise pour cette opération clé
Supposons qu'il existe une clé 1. Le système A doit définir Key1 sur ValueA, le système B doit définir Key1 sur ValueB et le système C doit définir Key1 sur ValueC. On espère que la valeur de Key1 changera dans l'ordre ValueA → Value B → Value C. À l’heure actuelle, nous devons enregistrer un horodatage lors de l’écriture des données dans la base de données. Supposons que l'horodatage soit le suivant :
1. Système A Clé 1 {ValeurA 3:00}
2 Système B Clé 1 {ValeurB 3:05}#🎜. 🎜## 🎜🎜#3. Clé 1 du système C {ValueC 3:10}
Donc, en supposant que le système B saisit le verrou en premier et règle la clé 1 sur {ValueB 3:05}. Si le système A saisit le verrou et constate que l'horodatage de ValueA est antérieur à l'horodatage dans le cache, l'opération Set ne sera pas effectuée. Et ainsi de suite.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!