Maison > base de données > Redis > le corps du texte

Résumé et partage de 20 questions d'entretien classiques sur Redis (avec analyse des réponses)

青灯夜游
Libérer: 2021-09-17 11:22:04
avant
8451 Les gens l'ont consulté

Golden Nine et Silver Ten arrivent bientôt. Cet article partagera avec vous 20 questions d'entretien classiques Redis. J'espère que cela vous sera utile !

Résumé et partage de 20 questions d'entretien classiques sur Redis (avec analyse des réponses)

1. Qu'est-ce que Redis ? A quoi sert-il principalement ?

Redis, le nom anglais complet est Remote Dictionary Server (Remote Dictionary Service), est une base de données de valeurs-clés de type journal open source écrite en langage ANSI C, prend en charge le réseau, peut être basée sur la mémoire et peut être persistant et fournit une API dans plusieurs langues. [Recommandations associées : Tutoriel vidéo Redis]

Différentes de la base de données MySQL, les données Redis sont stockées en mémoire. Ses vitesses de lecture et d'écriture sont très rapides et peuvent gérer plus de 100 000 opérations de lecture et d'écriture par seconde. Par conséquent, Redis est largement utilisé dans la mise en cache. De plus, Redis est également souvent utilisé pour les verrous distribués. De plus, Redis prend en charge les transactions, la persistance, les scripts LUA, les événements pilotés par LRU et diverses solutions de cluster.

2. Parlons des types de structure de données de base de Redis

La plupart des amis savent que Redis a les cinq types de base suivants :

String (String)
  • Hash (Hash)
  • List (List)
  • Set (set)
  • zset (ensemble ordonné)
  • Il dispose également de trois types de structures de données spéciaux

Geospatial
  • Hyperloglog
  • Bitmap
  • 2.1 Cinq types de données de base de Redis

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)Chaîne ( string)

Introduction : La chaîne est le type de structure de données le plus basique de Redis. Elle est binaire et peut stocker des images ou des objets sérialisés. La valeur maximale stockée est de 512 M
  • Exemple d'utilisation simple : définir la valeur de la clécode>, <code>get key, etc.
  • Scénarios d'application : session partagée, verrouillage distribué, compteur, limite de courant. set key valueget key
  • 应用场景:共享session、分布式锁,计数器、限流。
  • 内部编码有3种,int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)

C语言的字符串是char[]实现的,而Redis使用SDS(simple dynamic string) 封装,sds源码如下:

struct sdshdr{
  unsigned int len; // 标记buf的长度
  unsigned int free; //标记buf中未使用的元素个数
  char buf[]; // 存放元素的坑
}
Copier après la connexion

SDS 结构图如下:

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Redis为什么选择SDS结构,而C语言原生的 char[]不香吗?

举例其中一点,SDS中,O(1)时间复杂度,就可以获取字符串长度;而C 字符串,需要遍历整个字符串,时间复杂度为O(n)

Hash(哈希)

  • 简介:在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
  • 简单使用举例:hset key field valuehget key field
  • 内部编码:ziplist(压缩列表)hashtable(哈希表)
  • 应用场景:缓存用户信息等。
  • 注意点:如果开发使用hgetall,哈希元素比较多的话,可能导致Redis阻塞,可以使用hscan。而如果只是获取部分field,建议使用hmget。

字符串和哈希类型对比如下图:

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

List(列表)

  • 简介:列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
  • 简单实用举例: lpush key value [value ...]lrange key start end
  • Il existe 3 types d'encodage interne, int (entier long de 8 octets)/embstr (inférieur ou égal à une chaîne de 39 octets)/raw (chaîne supérieure à 39 octets)
  • C La chaîne de langage est implémentée par char[], et Redis utilise le package
  • SDS (simple dynamique chaîne)
Le code source sds est le suivant :

zadd user:ranking:2021-03-03 Jay 3
Copier après la connexion
Copier après la connexion

Le diagramme de structure SDS est le suivant :

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Pourquoi Redis a-t-il choisi la structure

SDS
Et le char[] natif du langage C n'est-il pas bon ?
  • Par exemple, en SDS, la longueur de la chaîne peut être obtenue avec une complexité temporelle O(1) ; tandis que pour les chaînes C, la chaîne entière doit être parcourue et la complexité temporelle est O(n)
  • < h4 data- id="heading-5">Hash (Hash)
  • Introduction : Dans Redis, le type de hachage fait référence à v (valeur) lui-même qui est également une structure de paire clé-valeur (k-v)
Exemple d'utilisation simple : hset key field value, hget key field

Encodage interne : ziplist (liste compressée), hashtable (table de hachage)< /code ></h4><p>Scénarios d'application : mise en cache des informations utilisateur, etc. <img src="https://img.php.cn/upload/image/656/664/457/1631501863989751.png" title="1631501863989751.png" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p>🎜Remarque🎜 : Si hgetall est utilisé dans le développement et qu'il existe de nombreux éléments de hachage, cela peut entraîner le blocage de Redis. Vous pouvez utiliser hscan. Si vous souhaitez uniquement obtenir certains champs, il est recommandé d'utiliser hmget. 🎜🎜🎜La comparaison entre les types de chaîne et de hachage est la suivante : 🎜🎜<img src="https://img.php.cn/upload/image/984/524/946/16315018485778Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" title="1631501848577830 .png " alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/>🎜<h4 data-id="heading-6">Liste (liste)🎜🎜🎜Introduction : Le type liste (liste) est utilisé pour stocker plusieurs chaînes ordonnées, une liste peut stocker jusqu'à 2 ^ 32-1 éléments. 🎜🎜Exemples simples et pratiques : <code> lpush key value [value ...], lrange key start end🎜🎜Encodage interne : ziplist (liste compressée), linkedlist (liste chaînée )🎜 🎜Scénarios d'application : file d'attente de messages, liste d'articles, 🎜🎜🎜Une image pour comprendre l'insertion et le pop-up du type de liste : 🎜🎜🎜🎜🎜Les scénarios d'application de liste font référence à ce qui suit : 🎜🎜🎜🎜lpush+lpop= Stack (stack)🎜🎜lpush +rpop=Queue (file d'attente)🎜🎜lpsh+ltrim=Capped Collection (collection limitée)🎜🎜lpush+brpop=Message Queue (file d'attente des messages)🎜🎜🎜🎜Set (set)🎜🎜🎜🎜
  • Introduction : Le type set est également utilisé pour enregistrer plusieurs éléments de chaîne, mais les éléments en double ne sont pas autorisés
  • Exemple d'utilisation simple : sadd key element [element ...], smembers key sadd key element [element ...]smembers key
  • 内部编码:intset(整数集合)hashtable(哈希表)
  • 注意点:smembers和lrange、hgetall都属于比较重的命令,如果元素过多存在阻塞Redis的可能性,可以使用sscan来完成。
  • 应用场景: 用户标签,生成随机数抽奖、社交需求。

有序集合(zset)

  • 简介:已排序的字符串集合,同时元素不能重复
  • 简单格式举例:zadd key score member [score member ...]zrank key member
  • 底层内部编码:ziplist(压缩列表)skiplist(跳跃表)
  • Encodage interne : intset (ensemble d'entiers), hashtable (table de hachage)
  • Notes : smembers, lrange et hgetall sont des commandes relativement lourdes. S'il y a trop d'éléments et qu'il existe une possibilité de bloquer Redis, vous pouvez utiliser sscan pour le terminer.

Scénarios d'application : tags utilisateur, génération de loterie à nombres aléatoires, besoins sociaux.

    Ensemble ordonné (zset)
  • Introduction : une collection de chaînes triées, et les éléments ne peuvent pas être répétés
  • Exemple de format simple : zadd key score member [ score membre ...], membre clé zrank
Encodage interne sous-jacent : ziplist (liste compressée), skiplist (liste ignorée)</code > code><h2 data-id="heading-10"></h2>Scénarios d'application : classements, besoins sociaux (tels que les likes des utilisateurs). <p><img src="https://img.php.cn/upload/image/701/100/216/1631501869718890.png" title="1631501869718890.png" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p>2.2 Trois types de données spéciaux de Redis<h3 data-id="heading-11"></h3><p>Geo : Introduit par Redis3.2, le positionnement géographique est utilisé pour stocker des informations de localisation géographique et opérer sur les informations stockées. </p><h3 data-id="heading-12">HyperLogLog : une structure de données utilisée pour les algorithmes statistiques de cardinalité, tels que UV pour les sites Web statistiques. </h3><p>Bitmaps : utilisez un bit pour mapper l'état d'un élément. Dans Redis, sa couche inférieure est basée sur le type de chaîne. Vous pouvez transformer les bitmaps en un tableau avec des bits comme unité</p><p><img src="https://img.php.cn/upload/image/959/396/733/1631501874129651.png" title="1631501874129651.png" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/>3. </p><h4 data-id="heading-13"></h4><p><img src="https://img.php.cn/upload/image/879/413/599/16315018818874Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" title="16315018818874Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/>3.1 Implémentation basée sur le stockage en mémoire</p><blockquote>Nous savons tous que la lecture et l'écriture en mémoire sont beaucoup plus rapides que la base de données Redis basée sur le stockage en mémoire, par rapport à la base de données MySQL où les données sont stockées sur le disque, élimine l'I/du disque. Ô consommation. <ul><li>3.2 Structure de données efficace</li><li>Nous savons que afin d'améliorer l'efficacité, l'index Mysql choisit la structure de données arborescente B+. En fait, une structure de données raisonnable peut rendre votre application/programme plus rapide. Jetons d'abord un coup d'œil à la structure des données et au diagramme d'encodage interne de Redis : </li><li></li><li></li>Chaîne dynamique simple SDS </ul></blockquote><h4 data-id="heading-14"></h4><p></p><h4 data-id="heading-15">Traitement de la longueur de chaîne : Redis obtient la longueur de la chaîne, la complexité temporelle est O(1) , et En langage C, il doit être parcouru depuis le début, et la complexité est O(n); </h4><p>Pré-allocation d'espace : plus la chaîne est modifiée fréquemment, plus l'allocation de mémoire sera fréquente, ce qui consomme des performances, et la modification SDS et l'expansion de l'espace nécessiteront une allocation supplémentaire. L'espace inutilisé réduit la perte de performances. <img src="https://img.php.cn/upload/image/349/649/661/163150189098463Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" title="163150189098463Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p> Libération d'espace paresseuse : lorsque SDS est raccourci, au lieu de recycler l'espace mémoire excédentaire, free enregistre l'espace excédentaire. S'il y a des modifications ultérieures, l'espace enregistré en free sera directement utilisé pour réduire l'allocation. <blockquote><ul>Sécurité binaire : Redis peut stocker certaines données binaires, chaînes rencontrées en langage C'<blockquote><ul><li>String : Si des nombres sont stockés, un encodage de type int est utilisé ; si des non-nombres sont stockés, une chaîne inférieure ou égale à 39 octets est embstr ; si elle est supérieure à 39 octets, un encodage brut est utilisé. </li><li>Liste : Si le nombre d'éléments dans la liste est inférieur à 512 et que la valeur de chaque élément de la liste est inférieure à 64 octets (par défaut), utilisez le codage ziplist, sinon utilisez le codage liste liée </li><li>Hash : Le nombre de Les éléments de type de hachage sont inférieurs à 512 , si toutes les valeurs sont inférieures à 64 octets, utilisez le codage ziplist, sinon utilisez le codage table de hachage. </li><li>Ensemble : si les éléments de l'ensemble sont tous des entiers et que le nombre d'éléments est inférieur à 512, utilisez le codage intset, sinon utilisez le codage table de hachage. </li><li>Zset : lorsque le nombre d'éléments dans l'ensemble ordonné est inférieur à 128 et que la valeur de chaque élément est inférieure à 64 octets, utilisez l'encodage ziplist, sinon utilisez l'encodage skiplist (skip list) </li></ul></blockquote><h3 data-id="heading-17">3.4 Modèle de thread raisonnable </h3> <p> <strong>Multiplexage d'E/S</strong></p><p><img src="https://img.php.cn/upload/image/222/494/553/1631501897175981.png" title="1631501897175981.png" alt="Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p><blockquote><p>La technologie de multiplexage d'E/S multiples permet à un seul thread de gérer efficacement plusieurs demandes de connexion, et Redis utilise epoll comme technologie de multiplexage d'E/S. De plus, le modèle de traitement des événements de Redis convertit les connexions, les lectures, les écritures et les arrêts dans epoll en événements, sans perdre trop de temps en E/S réseau. </p></blockquote><p>Qu'est-ce que le multiplexage d'E/S ? </p><blockquote><ul><li>E/S : E/S réseau</li><li>Multiple : plusieurs connexions réseau</li><li>Multiplexage : réutilisation du même fil. </li><li>Le multiplexage IO est en fait un modèle IO synchrone, qui implémente un thread capable de surveiller plusieurs descripteurs de fichiers ; une fois qu'un descripteur de fichier est prêt, il peut demander à l'application d'effectuer les opérations de lecture et d'écriture correspondantes sans descripteur de fichier. Lorsqu'il est prêt, le multiplexage IO est en fait un modèle IO synchrone, qui implémente un thread capable de surveiller plusieurs descripteurs de fichiers ; une fois qu'un descripteur de fichier est prêt, il peut demander à l'application d'effectuer les opérations de lecture et d'écriture correspondantes sans descripteur de fichier. l'application sera bloquée et le CPU sera remis. </li></ul></blockquote><p><strong>Modèle monothread</strong></p><ul><li>Redis est un modèle monothread, et le monothread évite les changements inutiles de contexte CPU et la consommation de verrouillage de concurrence. Précisément parce qu'il s'agit d'un seul thread, si une certaine commande est exécutée trop longtemps (comme la commande hgetall), cela provoquera un blocage. Redis est une base de données pour des scénarios d'exécution rapide. , les commandes telles que smembers, lrange, hgetall, etc. doivent donc être utilisées avec prudence. </li><li>Redis 6.0 introduit le multithreading pour accélérer, et son exécution de commandes et d'opérations de mémoire est toujours un seul thread. </li></ul><h3 data-id="heading-18">3.5 Mécanisme de mémoire virtuelle</h3><p>Redis construit directement le mécanisme de la VM par lui-même. Il n'appellera pas les fonctions système comme les systèmes ordinaires et ne perdra pas un certain temps en déplacements et en requêtes. </p><p><strong>Quel est le mécanisme de mémoire virtuelle de Redis ? </strong></p><blockquote><p>Le mécanisme de mémoire virtuelle échange temporairement les données rarement consultées (données froides) de la mémoire vers le disque, libérant ainsi un espace mémoire précieux pour d'autres données auxquelles il faut accéder (données chaudes). La fonction VM peut réaliser la séparation des données chaudes et froides, de sorte que les données chaudes soient toujours dans la mémoire et que les données froides soient enregistrées sur le disque. Cela peut éviter le problème de vitesse d'accès lente causée par une mémoire insuffisante. </p></blockquote><h2 data-id="heading-19">4. Qu'est-ce que la panne de cache, la pénétration du cache, l'avalanche de cache ? </h2><h3 data-id="heading-20">4.1 Problème de pénétration du cache</h3><p> Examinons d'abord une manière courante d'utiliser le cache : lorsqu'une demande de lecture arrive, vérifiez d'abord le cache s'il y a un succès dans le cache, il reviendra directement si le cache ne répond pas ; , vérifiez la base de données, puis placez la base de données. La valeur est mise à jour dans le cache puis renvoyée. </p><p><img src="https://img.php.cn/upload/image/546/664/907/1631501902654880.png" title="1631501902654880.png" alt="1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p><p><strong>Pénétration du cache</strong> : fait référence à l'interrogation de données qui n'existent certainement pas. Puisque le cache n'atteint pas, il doit être interrogé à partir de la base de données. Si les données ne sont pas trouvées, elles ne seront pas écrites. Le cache. Cela entraînera la suppression des données inexistantes. Chaque requête doit être interrogée dans la base de données, ce qui exerce une pression sur la base de données. </p><blockquote><p>Pour faire simple, lors de l'accès à une requête de lecture, ni le cache ni la base de données n'ont une certaine valeur, ce qui entraînera la pénétration de chaque requête pour cette valeur dans la base de données. </p></blockquote><p>La pénétration du cache est généralement causée par les situations suivantes : </p><ul><li><strong>Conception commerciale déraisonnable</strong>, par exemple, la plupart des utilisateurs n'ont pas de garde activé, mais chaque demande que vous faites va au cache et interroge un certain identifiant utilisateur. voir s'il y a une protection. </li><li><strong>Erreurs commerciales/d'exploitation et de maintenance/développement</strong>, telles que la suppression accidentelle des données du cache et de la base de données. </li><li><strong>Attaque de demandes illégales par des pirates</strong>, par exemple, les pirates informatiques fabriquent délibérément un grand nombre de demandes illégales pour lire des données commerciales inexistantes. </li></ul><p><strong>Comment éviter la pénétration du cache ? </strong> Généralement, il existe trois méthodes. </p><ul><li>1. S'il s'agit d'une demande illégale, nous vérifierons les paramètres à l'entrée de l'API et filtrerons les valeurs illégales. </li><li>2. Si la base de données de requêtes est vide, nous pouvons définir une valeur nulle ou une valeur par défaut pour le cache. Cependant, si une demande d'écriture arrive, le cache doit être mis à jour pour garantir la cohérence du cache. Dans le même temps, le délai d'expiration approprié est finalement défini pour le cache. (Couramment utilisé en entreprise, simple et efficace) </li><li>3. Utilisez le filtre Bloom pour déterminer rapidement si les données existent. Autrement dit, lorsqu'une requête de requête arrive, elle juge d'abord si la valeur existe via le filtre Bloom, puis continue de vérifier si elle existe. </li></ul><blockquote><p>Principe du filtre Bloom : Il se compose d'un tableau bitmap avec une valeur initiale de 0 et de N fonctions de hachage. Effectuez N algorithmes de hachage sur une clé pour obtenir N valeurs. Hachez ces N valeurs dans le tableau de bits et définissez-les sur 1. Puis lors de la vérification, si ces positions spécifiques sont toutes à 1, alors filtrage Bloom Le serveur détermine que la clé existe. . </p></blockquote><h3 data-id="heading-21">4.2 Problème de snowrun du cache</h3><p><strong>Cache snowrun : </strong> fait référence au délai d'expiration d'une grande quantité de données dans le cache, et les données de requête sont énormes, et les requêtes accèdent directement à la base de données, provoquant une pression excessive sur la base de données et même des temps d'arrêt. </p><ul><li>Les chutes de neige dans le cache sont généralement causées par l'expiration d'une grande quantité de données en même temps. Pour cette raison, elles peuvent être résolues en définissant le délai d'expiration de manière uniforme, c'est-à-dire en rendant le délai d'expiration relativement discret. Si vous utilisez une valeur fixe plus grande + une valeur aléatoire plus petite, 5 heures + 0 à 1800 secondes. </li><li>Un échec de Redis peut également provoquer des chutes de neige dans le cache. Cela nécessite la construction d'un cluster haute disponibilité Redis. </li></ul><h3 data-id="heading-22">4.3 Problème de panne de cache</h3><p><strong> Panne de cache : </strong> fait référence au moment où la clé du hotspot expire à un certain moment, et il se trouve qu'il y a un grand nombre de demandes simultanées pour cette clé à ce moment, donc un un grand nombre de requêtes sont atteintes dans la base de données. </p><p>La panne du cache semble un peu similaire. En fait, la différence entre eux est que le crash du cache signifie que la base de données est soumise à une pression excessive, voire en panne, et qu'elle est simplement due à un grand nombre de requêtes simultanées au niveau de la base de données. On peut considérer que la panne est un sous-ensemble du cache snowrun. Certains articles estiment que la différence entre les deux réside dans le fait que la panne vise un certain cache de touches de raccourci, tandis que Xuebeng cible de nombreuses clés. </p><p>Il existe deux solutions : </p><ul><li><strong>1. Utilisez le schéma de verrouillage mutex</strong>. Lorsque le cache échoue, au lieu de charger immédiatement les données de la base de données, vous utilisez d'abord certaines commandes d'opération atomiques avec un retour réussi, telles que (setnx de Redis) pour fonctionner. En cas de succès, chargez les données de la base de données et configurez le cache. Sinon, essayez à nouveau de récupérer le cache. </li><li><strong>2. "N'expire jamais"</strong> signifie qu'aucun délai d'expiration n'est défini, mais lorsque les données du hotspot sont sur le point d'expirer, le thread asynchrone se met à jour et définit le délai d'expiration. </li></ul><h2 data-id="heading-23">5. Quel est le problème de touche de raccourci et comment résoudre le problème de touche de raccourci</h2><p><strong>Qu'est-ce que la touche de raccourci</strong> ? Dans Redis, nous appelons les touches à fréquence d'accès élevée comme touches de raccourci. </p><p>Si une demande pour une certaine clé de hotspot est envoyée à l'hôte du serveur, en raison d'un volume de requêtes particulièrement important, cela peut entraîner des ressources hôte insuffisantes ou même des temps d'arrêt, affectant ainsi les services normaux. </p><p><img src="https://img.php.cn/upload/image/681/859/235/163150190722212Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" title="163150190722212Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" alt="1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p><p>Et comment la clé de hotspot est-elle générée ? Il y a deux raisons principales : </p><blockquote><ul><li>Les données consommées par les utilisateurs sont bien supérieures aux données produites, comme les ventes flash, les actualités brûlantes et d'autres scénarios où il y a plus de lecture et moins d'écriture. </li><li>Le partage des demandes est concentré, dépassant les performances d'un seul serveur Redi. Par exemple, les clés de nom fixe et les hachages tombent sur le même serveur, et la quantité d'accès instantané est énorme, dépassant le goulot d'étranglement de la machine et provoquant des problèmes de touches de raccourci. </li></ul></blockquote><p>Alors, comment identifier les raccourcis clavier dans le développement quotidien ? </p><blockquote><ul><li>Déterminez quelles touches de raccourci sont basées sur l'expérience ; </li><li>Rapport de statistiques sur les clients </li><li>Rapport à la couche d'agent de service</li></ul></blockquote><p>Comment résoudre le problème de touche de raccourci ? </p><blockquote><ul><li>Extension du cluster Redis : ajoutez des copies de fragments pour équilibrer le trafic de lecture ; </li><li>distribuez les touches de raccourci à différents serveurs ; </li><li>utilisez le cache de deuxième niveau, c'est-à-dire le cache local JVM, pour réduire les demandes de lecture Redis. </li></ul></blockquote><h2 data-id="heading-24">6. Stratégie d'expiration Redis et stratégie d'élimination de la mémoire</h2><p><img src="https://img.php.cn/upload/image/196/230/831/163150191159182Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" title="163150191159182Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)" alt="1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)"/></p><h3 data-id="heading-25">6.1 Stratégie d'expiration Redis</h3><p>Nous sommes là<code>set key的时候,可以给它设置一个过期时间,比如expire key 60. Spécifiez que cette clé expirera après 60 secondes. Comment Redis la gérera-t-il après 60 secondes ? Introduisons d'abord plusieurs stratégies d'expiration :

Expiration programmée

Chaque clé avec un délai d'expiration doit créer une minuterie, et la clé sera effacée immédiatement lorsque le délai d'expiration est atteint. Cette stratégie peut immédiatement effacer les données expirées et est très économe en mémoire ; cependant, elle occupera une grande quantité de ressources CPU pour traiter les données expirées, affectant ainsi le temps de réponse et le débit du cache.

Expiration paresseuse

Seulement lors de l'accès à une clé, il sera jugé si la clé a expiré, et elle sera effacée si elle expire. Cette stratégie peut économiser au maximum les ressources du processeur, mais elle est très peu respectueuse de la mémoire. Dans des cas extrêmes, un grand nombre de clés expirées ne seront plus accessibles, ne seront donc pas effacées et occuperont une grande quantité de mémoire.

Expiration périodique

À chaque fois, un certain nombre de clés dans le dictionnaire d'expiration d'un certain nombre de bases de données seront analysées et les clés expirées seront effacées. Cette stratégie est un compromis entre les deux premières. En ajustant l'intervalle de temps des analyses planifiées et la consommation de temps limitée de chaque analyse, l'équilibre optimal entre les ressources CPU et mémoire peut être atteint dans différentes circonstances.

Le dictionnaire expire enregistrera les données d'heure d'expiration de toutes les clés avec une heure d'expiration définie, où clé est un pointeur vers une clé dans l'espace clé et valeur est l'heure d'expiration représentée par l'horodatage UNIX de la clé avec une précision de la milliseconde. L'espace clé fait référence à toutes les clés enregistrées dans le cluster Redis.

Redis utilise à la fois l'expiration paresseuse et l'expiration périodiquedeux stratégies d'expiration.

  • Supposons que Redis stocke actuellement 300 000 clés et que toutes aient des délais d'expiration définis. Si vous vérifiez toutes les clés toutes les 100 ms, la charge du processeur sera extrêmement élevée et elle pourrait éventuellement se bloquer.
  • Par conséquent, redis utilise une expiration régulière et sélectionne au hasard un certain nombre de clés à vérifier et à supprimer toutes les 100 ms.
  • Cependant, il se peut que de nombreuses clés expirées n'aient finalement pas été supprimées. À l'heure actuelle, Redis utilise la suppression différée. Lorsque vous obtenez une clé, redis la vérifiera. Si la clé a un délai d'expiration défini et a expiré, elle sera supprimée à ce moment-là.

Mais si la suppression régulière manque un grand nombre de clés expirées, la suppression paresseuse ne sera pas utilisée. Il y aura beaucoup de clés expirées accumulées dans la mémoire, ce qui provoquera directement l'explosion de la mémoire. Ou parfois, lorsque le volume d'activité augmente, les clés Redis sont beaucoup utilisées, la mémoire n'est tout simplement pas suffisante et le responsable de l'exploitation et de la maintenance oublie d'augmenter la mémoire. Redis pourrait-il raccrocher comme ça ? Non! Redis se protège avec 8 stratégies d'élimination de mémoire ~

6.2 Stratégie d'élimination de mémoire Redis

  • volatile-lru : lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, utilisez LRU (la moins récemment utilisée) à partir de la clé avec un délai d'expiration set ) algorithme d'élimination ;
  • allkeys-lru : lorsque la mémoire est insuffisante pour accueillir les données nouvellement écrites, l'algorithme LRU (le moins récemment utilisé) est utilisé pour l'élimination de toutes les clés.
  • volatile-lfu : nouvellement ajouté dans la version 4.0, lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, l'algorithme LFU est utilisé pour supprimer les clés parmi les clés expirées.
  • allkeys-lfu : Nouveau dans la version 4.0, lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, l'algorithme LFU est utilisé pour éliminer de toutes les clés
  • volatile-random : Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites ; données nouvellement écrites, de Dans la clé avec un délai d'expiration défini, les données sont éliminées de manière aléatoire ;.
  • allkeys-random : lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, les données sont éliminées de manière aléatoire de toutes les clés.
  • volatile-ttl : Lorsque la mémoire n'est pas suffisante pour accueillir les données nouvellement écrites, la clé avec un délai d'expiration défini sera éliminée en fonction du délai d'expiration, et plus la date d'expiration sera éliminée en premier
  • noeviction : La stratégie par défaut, lorsque la mémoire est insuffisante Pour accueillir les données nouvellement écrites, la nouvelle opération d'écriture signalera une erreur.

7. Parlons des scénarios d'application courants de Redis

  • Cache
  • Liste de classement
  • Application de compteur
  • Session partagée
  • Verrouillage distribué
  • Réseau social
  • File d'attente des messages
  • Opération Bit

7.1 Mise en cache

Lorsque nous parlons de Redis, nous pensons naturellement au cache. Les sites Web de taille moyenne et grande au pays et à l'étranger sont indissociables du cache. Une utilisation raisonnable du cache, telle que la mise en cache des données des points d'accès, peut non seulement améliorer la vitesse d'accès du site Web, mais également réduire la pression sur la base de données. De plus, par rapport à Memcached, Redis fournit également des structures de données riches et des mécanismes de persistance tels que RDB et AOF, qui sont parmi les plus puissants.

7.2 Classements

Dans les applications Internet d'aujourd'hui, il existe différents classements, tels que les classements mensuels des ventes des sites de commerce électronique, les classements des cadeaux des applications sociales, les classements des votes des mini-programmes, etc. Le type zsetdata fourni par Redis peut implémenter ces classements complexes.

Par exemple, si les utilisateurs mettent en ligne des vidéos tous les jours, la liste de classement des likes peut être conçue comme ceci :

  • 1.用户Jay上传一个视频,获得6个赞,可以酱紫:
zadd user:ranking:2021-03-03 Jay 3
Copier après la connexion
Copier après la connexion
    1. 过了一段时间,再获得一个赞,可以这样:
zincrby user:ranking:2021-03-03 Jay 1
Copier après la connexion
    1. 如果某个用户John作弊,需要删除该用户:
zrem user:ranking:2021-03-03 John
Copier après la connexion
    1. 展示获取赞数最多的3个用户
zrevrangebyrank user:ranking:2021-03-03 0 2
Copier après la connexion

7.3 计数器应用

各大网站、APP应用经常需要计数器的功能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。

7.4 共享Session

如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。

7.5 分布式锁

几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。

  • 用synchronize或者reentrantlock本地锁肯定是不行的。
  • 如果是并发量不大话,使用数据库的悲观锁、乐观锁来实现没啥问题。
  • 但是在并发量高的场合中,利用数据库锁来控制资源的并发访问,会影响数据库的性能。
  • 实际上,可以用Redis的setnx来实现分布式的锁。

7.6 社交网络

赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适保存 这种类型的数据,Redis提供的数据结构可以相对比较容易地实现这些功能。

7.7 消息队列

消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。

7.8 位操作

用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多。这里要用到位操作——使用setbit、getbit、bitcount命令。原理是:redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统。

8. Redis 的持久化机制有哪些?优缺点说说

Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。

Redis提供了RDB和AOF两种持久化机制,它持久化文件加载流程如下:

1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

8.1 RDB

RDB,就是把内存数据以快照的形式保存到磁盘上。

什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。

RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:

1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

RDB 的优点

  • 适合大规模的数据恢复场景,如备份,全量复制等

RDB缺点

  • Il n'existe aucun moyen d'obtenir une persistance en temps réel/une persistance de deuxième niveau.
  • Les anciennes et nouvelles versions ont des problèmes de compatibilité au format RDB

AOF

AOF (ajouter uniquement le fichier) Persistance, utilisation du formulaire de journal pour enregistrer chaque opération d'écriture, ajouter au fichier et réexécuter le fichier AOF lorsque commande de redémarrage pour récupérer les données. Il résout principalement le problème en temps réel de la persistance des données. La valeur par défaut n'est pas activée.

Le flux de travail d'AOF est le suivant :

1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Avantages d'AOF

  • Cohérence et intégrité des données plus élevées

Inconvénients d'AOF

  • Plus il y a de contenu d'enregistrements AOF, meilleur est le fichier En gros, la récupération des données ralentit.

9. Comment atteindre la haute disponibilité de Redis ?

Nous utilisons Redis dans le projet, et nous ne déploierons certainement pas le service Redis à un seul moment. Car une fois le déploiement en point unique interrompu, il n’est plus disponible. Afin d'obtenir une haute disponibilité, une pratique courante consiste à copier plusieurs copies de la base de données et à les déployer sur différents serveurs. Si l'une d'entre elles tombe en panne, elle peut continuer à fournir des services. Redis dispose de trois modes de déploiement pour atteindre une haute disponibilité : Mode maître-esclave, mode sentinelle et mode cluster.

9.1 Mode maître-esclave

En mode maître-esclave, Redis déploie plusieurs machines, avec un nœud maître responsable des opérations de lecture et d'écriture et un nœud esclave responsable uniquement des opérations de lecture. Les données du nœud esclave proviennent du nœud maître. Le principe de mise en œuvre est le mécanisme de réplication maître-esclave

La réplication maître-esclave comprend la réplication complète et la réplication incrémentielle. Généralement, lorsque l'esclave commence à se connecter au maître pour la première fois, ou lorsque l'on considère que c'est la première fois qu'il se connecte, la copie complète est utilisée. Le processus de copie complète est le suivant :

1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • 1. . L'esclave envoie une commande de synchronisation au maître.
  • 2. Après avoir reçu la commande SYNC, le maître exécute la commande bgsave pour générer le fichier RDB complet.
  • 3. Le maître utilise un tampon pour enregistrer toutes les commandes d'écriture lors de la génération d'instantanés RDB.
  • 4. Une fois que le maître a exécuté bgsave, il envoie des fichiers d'instantanés RDB à tous les esclaves.
  • 5. Après avoir reçu le fichier d'instantané RDB, l'esclave charge et analyse l'instantané reçu.
  • 6. Le maître utilise un tampon pour enregistrer toutes les commandes écrites générées lors de la synchronisation RDB.
  • 7. Une fois l'instantané maître envoyé, il commence à envoyer la commande d'écriture dans le tampon à l'esclave ;
  • 8.salve accepte la demande de commande et exécute la commande d'écriture à partir du tampon maître

Après redis2.8. version, il a été utilisé psync pour remplacer sync, car la commande sync consomme des ressources système et psync est plus efficace.

Une fois l'esclave entièrement synchronisé avec le maître, si les données sur le maître sont à nouveau mises à jour, la réplication incrémentielle sera déclenchée.

Lorsque les données augmentent ou diminuent sur le nœud maître, replicationFeedSalves()函数,接下来在 Master节点上调用的每一个命令会使用replicationFeedSlaves() sera déclenché pour se synchroniser avec le nœud esclave. Avant d'exécuter cette fonction, le nœud maître déterminera si la commande exécutée par l'utilisateur a des mises à jour des données. S'il y a une mise à jour des données et que le nœud esclave n'est pas vide, cette fonction sera exécutée. La fonction de cette fonction est : Envoyer la commande exécutée par l'utilisateur à tous les nœuds esclaves, et laisser le nœud esclave l'exécuter. Le processus est le suivant :

1Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

9.2 Mode Sentinelle

En mode maître-esclave, une fois que le nœud maître ne peut pas fournir de services en raison d'une panne, il est nécessaire de promouvoir manuellement le nœud esclave en nœud maître, et à en même temps, informez l'application de mettre à jour l'adresse du nœud maître. De toute évidence, cette méthode de gestion des pannes est inacceptable dans la plupart des scénarios commerciaux. Redis a officiellement fourni l'architecture Redis Sentinel depuis la version 2.8 pour résoudre ce problème.

Mode Sentinel, un système Sentinel composé d'une ou plusieurs instances Sentinel, qui peut surveiller tous les nœuds maîtres et nœuds esclaves Redis, et lorsque le nœud maître surveillé entre dans l'état hors ligne, supprime automatiquement le maître hors ligne Un nœud esclave sous le serveur est mis à niveau vers un nouveau nœud maître. Cependant, si un processus sentinelle surveille un nœud Redis, des problèmes peuvent survenir (Problème à point unique). Par conséquent, plusieurs sentinelles peuvent être utilisées pour surveiller les nœuds Redis, et chaque sentinelle se surveillera également mutuellement.

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

En termes simples, le mode sentinelle a trois fonctions :

  • Envoyer des commandes et attendre que le serveur Redis (y compris le serveur maître et le serveur esclave) revienne pour surveiller son état d'exécution ; le nœud maître est en panne, et il basculera automatiquement le nœud esclave vers le nœud maître, puis informera les autres nœuds esclaves via le mode de publication et d'abonnement, modifiera le fichier de configuration et les laissera changer d'hôte
  • Les sentinelles les surveilleront également ; autre pour atteindre une haute disponibilité.
Quel est le processus de basculement

Supposons que le serveur principal soit en panne et que Sentinel 1 détecte ce résultat en premier. Le système n'effectuera pas immédiatement le processus de basculement. C'est simplement que Sentinel 1 croit subjectivement que le serveur principal est indisponible. Lorsque les sentinelles suivantes détectent également que le serveur principal est indisponible et que le nombre atteint une certaine valeur, un vote aura lieu entre les sentinelles. Le résultat du vote sera initié par une sentinelle pour effectuer une opération de basculement. Une fois le changement réussi, chaque sentinelle utilisera le mode publication-abonnement pour basculer le serveur esclave qu'elle surveille vers l'hôte. Ce processus est appelé objectif hors ligne. De cette façon, tout est transparent pour le client.

Le mode de fonctionnement de Sentinel est le suivant :

  • Chaque Sentinel envoie une commande PING au maître, à l'esclave et aux autres instances Sentinel qu'il connaît une fois par seconde.

  • Si le temps écoulé depuis la dernière réponse valide à la commande PING dépasse la valeur spécifiée par l'option down-after-milliseconds, l'instance sera marquée comme subjectivement hors ligne par Sentinel.

  • Si un Maître est marqué comme subjectif hors ligne, toutes les Sentinelles qui surveillent ce Maître doivent confirmer une fois par seconde que le Maître est effectivement entré dans l'état subjectif hors ligne.

  • Lorsqu'un nombre suffisant de Sentinelles (supérieur ou égal à la valeur spécifiée dans le fichier de configuration) confirment que le Maître est effectivement entré dans un état subjectif hors ligne dans la plage de temps spécifiée, le Maître sera marqué comme objectivement hors ligne.

  • Dans des circonstances normales, chaque Sentinelle enverra des commandes INFO à tous les maîtres et esclaves qu'elle connaît une fois toutes les 10 secondes.

  • Lorsque le maître est marqué comme objectivement hors ligne par Sentinel, la fréquence à laquelle Sentinel envoie des commandes INFO à tous les esclaves du maître hors ligne passera d'une fois toutes les 10 secondes à une fois toutes les secondes

  • S'il n'y en a pas assez Sentinelles doivent accepter Si le maître est hors ligne, le statut hors ligne objectif du maître sera supprimé ; si le maître renvoie à nouveau une réponse valide à la commande PING de Sentinel, le statut hors ligne subjectif du maître sera supprimé.

9.3 Mode cluster cluster

Le mode Sentinelle est basé sur le mode maître-esclave et réalise la séparation en lecture et en écriture. Il peut également basculer automatiquement et la disponibilité du système est plus élevée. Cependant, les données stockées dans chaque nœud sont les mêmes, ce qui gaspille de la mémoire et n'est pas facile à développer en ligne. Par conséquent, le cluster Cluster a vu le jour. Il a été ajouté dans Redis 3.0 et a implémenté le stockage distribué de Redis. Segmentez les données, ce qui signifie stocker différents contenus sur chaque nœud Redis pour résoudre le problème de l'expansion en ligne. De plus, il offre également des capacités de réplication et de basculement.

Communication des nœuds du cluster

Un cluster Redis se compose de plusieurs nœuds Comment chaque nœud communique-t-il entre eux ? Grâce au Protocole Gossip !

Le cluster Redis Cluster communique via le protocole Gossip. Les nœuds échangent en continu des informations, notamment les pannes de nœud, l'adhésion de nouveaux nœuds, les informations de changement de nœud maître-esclave, les informations d'emplacement, etc. Les messages de potins couramment utilisés sont divisés en quatre types : ping, pong, rencontre et échec.

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • message de rencontre : notifiez les nouveaux nœuds à rejoindre. L'expéditeur du message informe le destinataire de rejoindre le cluster actuel. Une fois la communication du message de rencontre terminée normalement, le nœud de réception rejoindra le cluster et effectuera des échanges périodiques de messages ping et pong.
  • Message Ping : message le plus fréquemment échangé dans le cluster. Chaque nœud du cluster envoie des messages ping à plusieurs autres nœuds chaque seconde, qui sont utilisés pour détecter si les nœuds sont en ligne et échanger des informations d'état entre eux.
  • message pong : lors de la réception d'un message ping ou d'une rencontre, il répondra à l'expéditeur sous forme de message de réponse pour confirmer la communication normale du message. Le message pong encapsule en interne ses propres données d'état. Un nœud peut également diffuser son propre message pong au cluster pour demander à l'ensemble du cluster de mettre à jour son propre statut.
  • Message d'échec : lorsqu'un nœud détermine qu'un autre nœud du cluster est hors ligne, il diffusera un message d'échec au cluster. Après avoir reçu le message d'échec, les autres nœuds mettront à jour le nœud correspondant vers l'état hors ligne.

Spécialement, chaque nœud communique avec d'autres nœuds via le bus cluster. Lors de la communication, utilisez un numéro de port spécial, c'est-à-dire le numéro de port du service externe plus 10 000. Par exemple, si le numéro de port d'un nœud est 6379, alors le numéro de port qu'il utilise pour communiquer avec d'autres nœuds est 16379. La communication entre les nœuds utilise un protocole binaire spécial.

Algorithme Hash Slot

Puisqu'il s'agit d'un stockage distribué, l'algorithme distribué utilisé par le cluster Cluster est Consistent Hash ? Non, c'est l'algorithme Hash Slot.

Algorithme de slotL'ensemble de la base de données est divisé en 16384 slots (slots). Chaque paire clé-valeur entrant dans Redis est hachée en fonction de la clé et attribuée à l'un de ces 16384 slots. La carte de hachage utilisée est également relativement simple. Elle utilise l'algorithme CRC16 pour calculer une valeur de 16 bits, puis modulo 16384. Chaque clé de la base de données appartient à l'un de ces 16 384 emplacements, et chaque nœud du cluster peut gérer ces 16 384 emplacements.

Chaque nœud du cluster est responsable d'une partie des emplacements de hachage. Par exemple, le cluster actuel a les nœuds A, B et C, et le nombre d'emplacements de hachage sur chaque nœud = 16384/3, alors il y a :

  • Le nœud A est responsable des emplacements de hachage 0 ~ 5460
  • Le nœud B est responsable des emplacements de hachage 5461 ~ 10922
  • Le nœud C est responsable des emplacements de hachage 10923 ~ 16383

Cluster Redis Cluster

Dans le cluster Redis Cluster, il faut s'assurer que 16384 emplacements correspondent. Tous les nœuds fonctionnent normalement. Si un nœud tombe en panne, l'emplacement dont il est responsable deviendra également invalide et l'ensemble du cluster ne fonctionnera pas.

Ainsi, afin d'assurer une haute disponibilité, le cluster Cluster introduit la réplication maître-esclave, et un nœud maître correspond à un ou plusieurs nœuds esclaves. Lorsque d'autres nœuds maîtres envoient une requête ping à un nœud maître A, si plus de la moitié des nœuds maîtres communiquent avec A expirent, alors le nœud maître A est considéré comme étant en panne. Si le nœud maître tombe en panne, le nœud esclave sera activé.

Sur chaque nœud de Redis, il y a deux choses, l'une est l'emplacement et sa plage de valeurs est 016383. L'autre est le cluster, qui peut être compris comme un plug-in de gestion de cluster. Lorsque la clé à laquelle nous accédons arrive, Redis obtiendra une valeur de 16 bits basée sur l'algorithme CRC16, puis prendra le résultat modulo 16384. Chaque clé dans Jiangzi correspond à un emplacement de hachage numéroté entre 016383. Utilisez cette valeur pour trouver le nœud correspondant à l'emplacement correspondant, puis accédez automatiquement au nœud correspondant pour les opérations d'accès.

Bien que les données soient stockées séparément sur différents nœuds, pour le client, l'ensemble du cluster est considéré comme un tout. Le client se connecte à n’importe quel nœud et ressemble à l’exploitation d’une seule instance de Redis. Lorsque la clé actionnée par le client n'est pas affectée au bon nœud, Redis renverra l'instruction de redirection et pointera finalement vers le bon nœud. C'est un peu comme le saut de redirection 302 de la page du navigateur.

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Failover

Le cluster Redis atteint une haute disponibilité Lorsqu'un nœud du cluster tombe en panne, failover est utilisé pour garantir que le cluster peut fournir des services externes normaux.

Le cluster Redis réalise la découverte de pannes via des messages ping/pong. Cet environnement comprend subjectif hors ligne et objectif hors ligne.

Hors ligne subjectif : Un nœud pense qu'un autre nœud n'est pas disponible, c'est-à-dire qu'il est dans un état hors ligne. Cet état n'est pas la détermination finale du défaut et ne peut représenter que l'opinion d'un nœud, et il peut y avoir une erreur de jugement.

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Objectif hors ligne : Indique qu'un nœud est véritablement hors ligne. Plusieurs nœuds du cluster pensent que le nœud est indisponible, parvenant ainsi à un consensus. Si le nœud maître détenant l'emplacement tombe en panne, un basculement doit être effectué pour le nœud.

  • Supposons que le nœud A marque le nœud B comme subjectivement hors ligne. Après un certain temps, le nœud A envoie l'état du nœud B à d'autres nœuds via des messages lorsque le nœud C reçoit le message et analyse le corps du message, si le nœud B est trouvé. Lorsque l'état pfail se produit, le processus objectif hors ligne sera déclenché ;
  • Lorsque le nœud maître est hors ligne, le cluster Redis Cluster votera pour le nœud maître détenant l'emplacement de statistiques pour voir si le nombre de votes atteint la moitié. les statistiques du rapport sont supérieures à la moitié, marquées comme statut objectif hors ligne.

Le processus est le suivant :

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Récupération après panne : Une fois la panne découverte, si le nœud hors ligne est le nœud maître, vous devez choisir l'un de ses nœuds esclaves pour le remplacer afin d'assurer le niveau élevé disponibilité du cluster. Le processus est le suivant :

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • Contrôle de qualification : Vérifiez si le nœud esclave a les conditions pour remplacer le nœud maître défaillant.
  • Préparer l'heure des élections : une fois le contrôle de qualification réussi, mettez à jour l'heure des élections en cas d'échec du déclenchement.
  • Initier une élection : Lorsque le moment de l'élection de faute arrive, organisez une élection.
  • Vote d'élection : Seul le nœud maître détenant le slot dispose de votes. Lorsque les nœuds esclaves collectent suffisamment de votes (plus de la moitié), l'opération de remplacement du nœud maître est déclenchée

10. Verrou distribué Redis ? Quels sont les points auxquels il faut prêter attention ?

Le verrouillage distribué est une implémentation d'un verrou qui contrôle différents processus dans un système distribué pour accéder conjointement aux ressources partagées. Les scénarios commerciaux tels que passer des commandes flash, récupérer des enveloppes rouges, etc. nécessitent tous l'utilisation de verrous distribués. Redis est souvent utilisé comme verrou distribué dans nos projets.

选了Redis分布式锁的几种实现方法,大家来讨论下,看有没有啥问题哈。

  • 命令setnx + expire分开写
  • setnx + value值是过期时间
  • set的扩展命令(set ex px nx)
  • set ex px nx + 校验唯一随机值,再删除

10.1 命令setnx + expire分开写

if(jedis.setnx(key,lock_value) == 1){ //加锁
    expire(key,100); //设置过期时间
    try {
        do something  //业务请求
    }catch(){
  }
  finally {
       jedis.del(key); //释放锁
    }
}
Copier après la connexion

如果执行完setnx加锁,正要执行expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。

10.2 setnx + value值是过期时间

long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间
String expiresStr = String.valueOf(expires);

// 如果当前锁不存在,返回加锁成功
if (jedis.setnx(key, expiresStr) == 1) {
        return true;
} 
// 如果锁已经存在,获取锁的过期时间
String currentValueStr = jedis.get(key);

// 如果获取到的过期时间,小于系统当前时间,表示已经过期
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {

     // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈)
    String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
    
    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
         // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁
         return true;
    }
}
        
//其他情况,均返回加锁失败
return false;
}
Copier après la connexion

笔者看过有开发小伙伴是这么实现分布式锁的,但是这种方案也有这些缺点

  • 过期时间是客户端自己生成的,分布式环境下,每个客户端的时间必须同步。
  • 没有保存持有者的唯一标识,可能被别的客户端释放/解锁。
  • 锁过期的时候,并发多个客户端同时请求过来,都执行了jedis.getSet(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。

10.3: set的扩展命令(set ex px nx)(注意可能存在的问题)

if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ //加锁
    try {
        do something  //业务处理
    }catch(){
  }
  finally {
       jedis.del(key); //释放锁
    }
}
Copier après la connexion

这个方案可能存在这样的问题:

  • 锁过期释放了,业务还没执行完。
  • 锁被别的线程误删。

10.4 set ex px nx + 校验唯一随机值,再删除

if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ //加锁
    try {
        do something  //业务处理
    }catch(){
  }
  finally {
       //判断是不是当前线程加的锁,是才释放
       if (uni_request_id.equals(jedis.get(key))) {
        jedis.del(key); //释放锁
        }
    }
}
Copier après la connexion

在这里,判断当前线程加的锁和释放锁是不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

一般也是用lua脚本代替。lua脚本如下:

if redis.call(&#39;get&#39;,KEYS[1]) == ARGV[1] then 
   return redis.call(&#39;del&#39;,KEYS[1]) 
else
   return 0
end;
Copier après la connexion

这种方式比较不错了,一般情况下,已经可以使用这种实现方式。但是存在锁过期释放了,业务还没执行完的问题(实际上,估算个业务处理的时间,一般没啥问题了)。

11. 使用过Redisson嘛?说说它的原理

分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

当前开源框架Redisson就解决了这个分布式锁问题。我们一起来看下Redisson底层原理是怎样的吧:

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。

12. 什么是Redlock算法

Redis一般都是集群部署的,假设数据在主从同步过程,主节点挂了,Redis分布式锁可能会有哪些问题呢?一起来看些这个流程图:

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。

为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的:

搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。

我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。

2Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

RedLock的实现步骤:如下

  • 1. Obtenez l'heure actuelle en millisecondes.
  • 2. Demandez des verrous à 5 nœuds maîtres dans l'ordre. Le client définit la connexion réseau et le délai d'expiration de réponse, et le délai d'expiration doit être inférieur au délai d'expiration du verrou. (En supposant que le délai d'expiration du verrouillage automatique est de 10 secondes, le délai d'expiration est généralement compris entre 5 et 50 millisecondes. Supposons que le délai d'expiration soit de 50 ms). Si le délai expire, ignorez le nœud maître et essayez le nœud maître suivant dès que possible.
  • 3. Le client utilise l'heure actuelle moins l'heure de début d'acquisition du verrou (c'est-à-dire l'heure enregistrée à l'étape 1) pour obtenir l'heure utilisée pour acquérir le verrou. Le verrou est acquis avec succès si et seulement si plus de la moitié (N/2+1, ici 5/2+1=3 nœuds) des nœuds maîtres Redis ont obtenu le verrou, et que le temps d'utilisation est inférieur au délai d'expiration du verrou. . (Comme le montre l'image ci-dessus, 10s>30ms+40ms+50ms+4m0s+50ms)
  • Si le verrou est obtenu, le temps effectif réel de la clé change et le temps utilisé pour obtenir le verrou doit être soustrait.
  • Si l'acquisition du verrou échoue (le verrou n'est pas acquis sur au moins N/2+1 instances maîtres, ou le temps d'acquisition du verrou a dépassé le temps effectif), le client doit déverrouiller sur tous les nœuds maîtres (même si certains nœuds maîtres ne pas acquérir le verrou du tout) Même si le verrou ne réussit pas, il doit quand même être déverrouillé pour empêcher certains poissons de glisser à travers le filet).

Les étapes simplifiées sont les suivantes :

  • Demander des verrous à 5 nœuds maîtres dans l'ordre
  • Déterminez s'il faut ignorer le nœud maître en fonction du délai d'expiration défini.
  • Si plus de trois nœuds ou égaux sont verrouillés avec succès et que la durée d'utilisation est inférieure à la période de validité du verrou, on peut considérer que le verrouillage est réussi.
  • Si l'acquisition du verrou échoue, déverrouillez-le !

13. Table de saut de Redis

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • La table de saut est l'une des implémentations sous-jacentes de l'ensemble ordonné zset
  • La table de saut prend en charge les nœuds avec une moyenne O(logN) et le pire des cas O(N ) complexité Rechercher, ainsi que traiter par lots les nœuds via des opérations séquentielles.
  • L'implémentation de la liste à sauter se compose de deux structures : zskiplist et zskiplistNode, où zskiplist est utilisé pour enregistrer les informations de la table à sauter (telles que le nœud d'en-tête, le nœud de queue, la longueur) et zskiplistNode est utilisé pour représenter les nœuds de la liste à sauter.
  • La liste de sauts est basée sur la liste chaînée, ajoutant des index à plusieurs niveaux pour améliorer l'efficacité de la recherche.

14. Comment MySQL et Redis assurent-ils la cohérence des doubles écritures

  • Double suppression retardée du cache
  • Mécanisme de nouvelle tentative de suppression du cache
  • Lire le biglog et supprimer le cache de manière asynchrone

14.1 Double suppression retardée ?

Qu'est-ce que la double suppression différée ? L'organigramme est le suivant :

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • Supprimez d'abord le cache

  • puis mettez à jour la base de données

  • Dormez pendant un moment (par exemple 1 seconde) et supprimez à nouveau le cache.

Combien de temps faut-il habituellement pour dormir pendant un certain temps ? Sont-ils tous 1 seconde ?

Ce temps de veille = le temps nécessaire pour lire les données de la logique métier + quelques centaines de millisecondes. Afin de garantir la fin de la demande de lecture, la demande d'écriture peut supprimer les données sales mises en cache qui peuvent être apportées par la demande de lecture.

Cette solution n'est pas mauvaise. Ce n'est que pendant la période de sommeil (par exemple, seulement 1 seconde) qu'il peut y avoir des données sales, et les entreprises générales l'accepteront. Mais que se passe-t-il si la suppression du cache échoue une deuxième fois ? Les données du cache et de la base de données peuvent toujours être incohérentes, n'est-ce pas ? Que diriez-vous de définir un délai d’expiration naturel pour la clé et de la laisser expirer automatiquement ? L’entreprise doit-elle accepter des incohérences de données pendant la période d’expiration ? Ou existe-t-il une autre meilleure solution ?

14.2 Mécanisme de nouvelle tentative de suppression du cache

En raison d'une double suppression retardée, la deuxième étape de suppression du cache peut échouer, entraînant une incohérence des données. Vous pouvez utiliser cette solution pour optimiser : si la suppression échoue, supprimez-la simplement quelques fois de plus pour vous assurer que la suppression du cache est réussie. Vous pouvez donc introduire un mécanisme de nouvelle tentative de suppression du cache

3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

  • demande d'écriture pour mettre à jour le base de données

  • Le cache est dû à un certain Pour certaines raisons, la suppression a échoué

  • Mettez la clé qui n'a pas pu être supprimée dans la file d'attente des messages

  • Consommez le message dans la file d'attente des messages et obtenez la clé à supprimer

  • Réessayez l'opération de suppression du cache

14.3 Lire le biglog Le mécanisme de suppression asynchrone du cache

réessayez le mécanisme de suppression du cache est correct, mais il provoquera de nombreuses intrusions dans le code métier. En fait, il peut également être optimisé de cette manière : éliminer les clés de manière asynchrone via le binlog de la base de données.

3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Prenons MySQL comme exemple

  • Vous pouvez utiliser le canal d'Alibaba pour collecter les journaux binlog et les envoyer à la file d'attente MQ
  • Ensuite, confirmez et traitez le message de mise à jour via le mécanisme ACK, supprimez le cache et assurez la cohérence du cache des données

15 ​​Pourquoi Redis a-t-il changé. au multi-thread après 6.0 ?

  • Avant Redis 6.0, lorsque Redis traitait les requêtes des clients, y compris la lecture des sockets, l'analyse, l'exécution, l'écriture des sockets, etc., elles étaient toutes traitées par un thread principal séquentiel et série. C'est ce qu'on appelle le « thread unique ».
  • Pourquoi le multithread n'était-il pas utilisé avant Redis6.0 ? Lors de l'utilisation de Redis, il n'y a presque aucune situation dans laquelle le processeur devient un goulot d'étranglement. Redis est principalement limité par la mémoire et le réseau. Par exemple, sur un système Linux normal, Redis peut gérer 1 million de requêtes par seconde en utilisant le pipeline, donc si l'application utilise principalement des commandes O(N) ou O(log(N)), elle ne consommera guère beaucoup de CPU.

L'utilisation du multi-threading par Redis ne signifie pas qu'il abandonne complètement le monothreading. Redis utilise toujours un modèle monothread pour traiter les demandes des clients. Il utilise uniquement le multithreading pour gérer la lecture et l'écriture des données et l'analyse des protocoles. il utilise toujours un seul thread pour exécuter les commandes.

Le but de ceci est que le goulot d'étranglement des performances de Redis réside dans les E/S du réseau plutôt que dans le processeur. L'utilisation du multithreading peut améliorer l'efficacité de la lecture et de l'écriture des E/S, améliorant ainsi les performances globales de Redis.

16. Parlons du mécanisme de transaction Redis

Redis implémente le mécanisme de transaction via un ensemble de commandes telles que MULTI, EXEC, WATCH. Les transactions prennent en charge l'exécution de plusieurs commandes à la fois, et toutes les commandes d'une transaction seront sérialisées. Pendant le processus d'exécution de la transaction, les commandes dans la file d'attente seront exécutées en série dans l'ordre et les demandes de commandes soumises par d'autres clients ne seront pas insérées dans la séquence de commandes d'exécution de la transaction.

En bref, une transaction Redis est l'exécution séquentielle, unique et exclusived'une série de commandes dans une file d'attente.

Le processus d'exécution des transactions dans Redis est le suivant :

  • Démarrer la transaction (MULTI)
  • Mise en file d'attente des commandes
  • Exécuter la transaction (EXEC), annuler la transaction (DISCARD)
Commande Description
EXEC Exécuter toutes les commandes dans le bloc de transaction
DISCARD Annuler la transaction et abandonner l'exécution de toutes les commandes dans le bloc de transaction
MULTI Marquer le début d'un bloc de transaction
UNWATCH Annuler La commande WATCH surveille toutes les touches.
WATCH Clé de surveillance. Si la clé est modifiée par d'autres commandes avant l'exécution de la transaction, la transaction sera interrompue.

17. Comment gérer les conflits de hachage dans Redis

Redis est une base de données K-V en mémoire, qui utilise un hachage global pour enregistrer toutes les paires clé-valeur. Cette table de hachage est composée de plusieurs compartiments de hachage. L'élément d'entrée dans le compartiment de hachage stocke les pointeurs key et value, où *key pointe vers la clé réelle et *value pointe vers la valeur réelle.

3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

La vitesse de recherche des tables de hachage est très rapide, quelque peu similaire à HashMap en Java, ce qui nous permet de trouver rapidement des paires clé-valeur dans une complexité temporelle O(1). Tout d'abord, calculez la valeur de hachage via la clé, recherchez l'emplacement du compartiment de hachage correspondant, puis localisez l'entrée et recherchez les données correspondantes dans l'entrée.

Qu'est-ce qu'une collision de hachage ?

Conflit de hachage : la même valeur de hachage est calculée via différentes clés, ce qui donne le même compartiment de hachage.

Afin de résoudre les conflits de hachage, Redis utilise Chain Hash. Le hachage en chaîne signifie que plusieurs éléments du même compartiment de hachage sont stockés dans une liste chaînée et qu'ils sont connectés tour à tour à l'aide de pointeurs.

3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Certains lecteurs peuvent encore avoir des questions : les éléments de la chaîne de conflits de hachage ne peuvent être recherchés qu'un par un via des pointeurs puis exploités. Lorsqu'une grande quantité de données est insérée dans la table de hachage, plus il y aura de conflits, plus la liste chaînée des conflits sera longue et l'efficacité des requêtes sera réduite.

Afin de maintenir l'efficacité, Redis effectuera une opération de rehachage sur la hash table, ce qui signifie ajouter des compartiments de hachage et réduire les conflits. Afin de rendre le rehachage plus efficace, Redis utilise également deux tables de hachage globales par défaut, une pour l'utilisation actuelle, appelée table de hachage principale, et une pour l'expansion, appelée table de hachage de sauvegarde. 18. Lors de la génération RDB, Redis peut-il gérer les requêtes d'écriture en même temps ?

Oui

, Redis fournit deux instructions pour générer du RDB, à savoir save et bgsave.

S'il s'agit d'une instruction de sauvegarde, elle bloquera car elle est exécutée par le thread principal.
  • S'il s'agit d'une instruction bgsave, elle crée un processus enfant pour écrire le fichier RDB. La persistance de l'instantané est entièrement gérée par le processus enfant et le processus parent peut continuer à traiter les demandes des clients.
  • 19. Quel protocole est utilisé au bas de Redis ?

RESP, le nom anglais complet est Redis Serialization Protocol, qui est un ensemble de protocoles de sérialisation spécialement conçus pour redis. Ce protocole est en fait apparu dans la version 1.2 de redis. mais ce n'est qu'avec Redis2.0 qu'il est finalement devenu la norme pour le protocole de communication Redis.

RESP présente principalement les avantages d'une mise en œuvre simple, d'une vitesse d'analyse rapide et d'une bonne lisibilité

.

20. Filtre Bloom

Pour résoudre le problème de

pénétration du cache

, nous pouvons utiliser le

Filtre Bloom. Qu'est-ce qu'un filtre bloom ? Un filtre Bloom est une structure de données qui prend très peu de place. Il se compose d'un long vecteur binaire et d'un ensemble de fonctions de mappage de hachage. Il est utilisé pour récupérer si un élément est dans un ensemble, l'efficacité spatiale et le temps de requête. Ils sont bien meilleurs que les algorithmes ordinaires. Les inconvénients sont un certain taux de mauvaise reconnaissance et des difficultés de suppression.

Quel est le principe du filtre Bloom ?

Supposons que nous ayons un ensemble A et qu'il y ait n éléments dans A. À l'aide des fonctions de hachage

k , chaque élément de A est mappé à différentes positions dans un tableau B d'une longueur d'un bit, et les nombres binaires à ces positions sont tous définis sur 1. Si l'élément à vérifier est mappé par ces k fonctions de hachage et qu'il s'avère que les nombres binaires à ses k positions sont tous 1, cet élément appartient probablement à l'ensemble A. Au contraire, ne doit pas appartenir à l'ensemble A. . Regardons un exemple simple. Supposons que l'ensemble A comporte 3 éléments, à savoir {d1, d2, d3

}. Il existe 1 fonction de hachage, qui est

Hash1. Mappez maintenant chaque élément de A à un tableau B de longueur 16 bits.

Nous mappons maintenant d1. En supposant que Hash1(d1) = 2, nous changeons la grille avec l'indice 2 dans le tableau B en 1, comme suit : 3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Nous mappons maintenant 3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)d2

Également mappé, en supposant que Hash1. (d2) = 5, nous changeons la grille d'indice 5 dans le tableau B en 1, comme suit :

Ensuite, nous mappons également 3Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)d3

, en supposant que Hash1(d3 ) est également égal à 2, ce qui marque également le grille avec l'indice 2 comme 1 :

Par conséquent, nous devons confirmer si un élément dn est dans l'ensemble A. Il nous suffit de calculer l'indice d'index obtenu par Hash1 (dn). Tant qu'il est 0, cela signifie que cet élément n'est pas dans l'ensemble A. . Et si l'indice d'index est 1 ? Alors l’élément peut être un élément de A. Parce que vous voyez, les valeurs d'indice obtenues par d1 et d3 peuvent toutes deux être 1, ou elles peuvent être mappées par d'autres nombres. Le filtre Bloom a cet inconvénient : il y aura des faux positifs causés par une collision de hachage, là. est une erreur de jugement.

Comment réduire cette erreur ?

  • Créez davantage de mappages de fonctions de hachage pour réduire la probabilité de collision de hachage
  • Dans le même temps, augmenter la longueur en bits du tableau B peut augmenter la plage de données générées par la fonction de hachage et également réduire la probabilité de collision de hachage

Nous ajoutons une autre fonction Hash2hash map. Supposons que Hash2(d1)=6, Hash2(d3)=8 ne seront pas en conflit, comme suit :

Résumé et partage de 20 questions dentretien classiques sur Redis (avec analyse des réponses)

Même s'il y a une erreur, nous pouvons trouver. , le filtre Bloom ne stocke pas les données complètes, il utilise simplement une série de fonctions de carte de hachage pour calculer la position, puis remplit le vecteur binaire. Si le nombre est grand, le filtre Bloom peut économiser beaucoup d'espace de stockage grâce à un très faible taux d'erreur, ce qui est assez rentable. Actuellement, les filtres Bloom disposent déjà de bibliothèques open source qui implémentent les implémentations correspondantes, telles que la

la bibliothèque de classes Guava de Google

et la bibliothèque de classes Algebird de Twitter, qui sont facilement disponibles, ou vous pouvez implémenter votre propre conception basée sur les Bitmaps fournis avec Redis. . Pour plus de connaissances sur la programmation, veuillez visiter :

Vidéos de programmation

 ! !

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!

Étiquettes associées:
source:juejin.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal