Maison  >  Article  >  base de données  >  Une brève analyse des modèles monothread et multithread dans Redis6

Une brève analyse des modèles monothread et multithread dans Redis6

青灯夜游
青灯夜游avant
2022-01-26 10:38:023309parcourir

Cet article vous amènera à comprendre le modèle de thread dans Redis6 et à présenter le modèle monothread et le modèle multithread. J'espère qu'il sera utile à tout le monde !

Une brève analyse des modèles monothread et multithread dans Redis6

1. Historique d'évolution de Redis

Si nous disons simplement que redis est monothread ou multithread, cette réponse n'est certainement pas rigoureuse. Les modèles de thread utilisés par les différentes versions sont différents. [Recommandations associées : Tutoriel vidéo Redis]

Une brève analyse des modèles monothread et multithread dans Redis6

  • Version 3.x, la première version, qui est le redis de bouche à oreille, est monothread.

  • La version 4.x, à proprement parler, n'est pas monothread, mais le thread responsable du traitement des demandes des clients est monothread, mais elle a commencé à ajouter quelques éléments multithread (suppression asynchrone) . <code>多线程的东西(异步删除)

  • 最新版本的6.0.x后, 告别了大家印象中的单线程,用一种全新的多线程来解决问题。

2. Redis单线程模型

2.1 单线程真实含义

主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。这也是Redis对外提供键值存储服务的主要流程。Une brève analyse des modèles monothread et multithread dans Redis6
但Redis的其他功能, 比如持久化、异步删除、集群数据同步等等,其实是由额外的线程执行的。  可以这么说,Redis工作线程是单线程的。但是,整个Redis来说,是多线程的

2.2 单线程性能快原因

Redis 3.x 单线程时代但是性能很快的主要原因

  • 基于内存操作:所有数据都存在内存中,因此所有的运算都是内存级别的
  • 数据结构简单:Redis的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是o(1)
  • 多路复用和费阻塞IO:使用IO多路复用功能监听多个socket连接客户端,这样就可以使用一个线程连接处理多个请求,减少线程切换带来的开销,同时避免IO阻塞操作
  • 避免上下文切换:因为是单线程模型,就可以避免不必要的上先文切换和多线程竞争,这样可以省去多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生

2.3 采用单线程原因

Redis 是基于内存操作的, 因此他的瓶颈可能是机器的内存或者网络带宽而并非 CPU ,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了,况且使用多线程比较麻烦。 但是在 Redis 4.0 中开始支持多线程了,例如后台删除等功能
简单来说,Redis  4.0 之前一直采用单线程的主要原因有以下三个: 

  • 使用单线程模型是 Redis 的开发和维护更简单,因为单线程模型方便开发和调试;多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,因此没有必要使用多线程。单线程机制使得 Redis 内部实现的复杂度大大降低,Hash 的惰性 Rehash、Lpush 等等 “线程不安全” 的命令都可以无锁进行。 

  • 即使使用单线程模型也并发的处理多客户端的请求,主要使用的是多路复用和非阻塞 IO; 

  • 对于 Redis 系统来说, 主要的性能瓶颈是内存或者网络带宽而并非 CPU

Après la dernière version 6.0.

2. Modèle Redis à thread unique

2.1 La véritable signification du modèle à thread unique🎜🎜🎜🎜 signifie principalement que les E/S réseau de Redis et la lecture et l'écriture des paires clé-valeur sont complétées par un seul thread, et Redis traite les demandes des clients, y compris l'acquisition (lecture du socket), l'analyse, l'exécution, le retour de contenu (écriture du socket), etc., sont toutes traitées par un thread principal séquentiel et série, appelé « thread unique ». ". Il s'agit également du processus principal permettant à Redis de fournir des services de stockage externes clé-valeur. Une brève analyse des modèles monothread et multithread dans Redis6
Mais d'autres fonctions de Redis, telles que la 🎜persistance, la suppression asynchrone, la synchronisation des données du cluster, etc.🎜, sont en fait exécutées par des threads supplémentaires.​ On peut dire que le thread de travail Redis est monothread. Cependant, l'ensemble de Redis est multi-thread ; code>les performances sont rapides  :🎜
    🎜Basé sur les opérations de mémoire : toutes les données sont stockées en mémoire, donc toutes les opérations sont au niveau de la mémoire🎜🎜Structure de données simple : la structure de données de Redis est spécialement conçue, et ces simples data La plupart du temps, la complexité de la recherche et du fonctionnement de la structure est o(1)🎜🎜Multiplexage et blocage des IO : utilisez la fonction de multiplexage IO pour surveiller les connexions multiples de socket au client, de cette façon, un thread La connexion peut être utilisée pour traiter plusieurs requêtes, réduisant ainsi la surcharge causée par le changement de thread, tout en évitant les opérations de blocage d'E/S. peut économiser le temps et la consommation de performances causés par la commutation multithread, et un seul thread ne causera pas de problèmes de blocage🎜🎜🎜🎜🎜2.3 Raisons de l'utilisation d'un seul thread🎜🎜🎜🎜Redis est basé sur le fonctionnement de la mémoire, donc son goulot d'étranglement peut être la mémoire de la machine ou la bande passante du réseau plutôt que le CPU Puisque le CPU n'est pas le goulot d'étranglement, il est naturel d'utiliser une solution monothread, et il est plus gênant d'utiliser plusieurs threads. Cependant, dans Redis 4.0, il a commencé à prendre en charge le multi-threading, tel que la suppression en arrière-plan et d'autres fonctions.
    Pour faire simple, il y a trois raisons principales pour lesquelles le monothreading a été utilisé avant Redis 4.0 : 🎜🎜🎜🎜L'utilisation d'un modèle monothread simplifie le développement et la maintenance de Redis, car le modèle monothread est pratique pour le développement et le débogage ; Bien que le modèle de thread fonctionne bien à certains égards, il introduit une incertitude dans l'ordre d'exécution du programme, entraîne une série de problèmes de lecture et d'écriture simultanées, augmente la complexité du système et peut impliquer un changement de thread ou même verrouillage et déverrouillage, perte de performances causée par un blocage. Redis a des performances de traitement très élevées grâce au modèle d'événement AE, au multiplexage d'E/S et à d'autres technologies, il n'est donc pas nécessaire d'utiliser le multithreading. Le mécanisme à thread unique réduit considérablement la complexité de l'implémentation interne de Redis. L'inertie de Hash, Rehash, Lpush et d'autres commandes « thread-unsafe » peuvent être exécutées sans verrous. 🎜🎜🎜🎜Même en utilisant un modèle monothread, plusieurs requêtes client peuvent être traitées simultanément, principalement en utilisant le multiplexage et les E/S non bloquantes 🎜🎜🎜🎜Pour le système Redis, le principal goulot d'étranglement des performances est la mémoire ou la bande passante réseau au lieu du processeur ; . 🎜🎜🎜🎜🎜3. Modèle multi-thread Redis🎜🎜🎜🎜🎜3.1 Raisons de l'introduction du multi-threading🎜🎜🎜

    Puisque le monothread est si bon, pourquoi introduire le multithreading ? Un seul thread a également ses propres problèmes, tels que 既然单线程那么好,为啥又要引入多线程?单线程也有自己的烦恼,比如大key删除问题: 正常情况下使用 del 指令可以很快的删除数据,而当被删除的 key 是一个非常大的对象时,例如时包含了成千上万个元素的 hash 集合时,那么 del 指令就会造成 Redis 主线程卡顿。

    因此,在 Redis 4.0 中就新增了多线程的模块,当然此版本中的多线程主要是为了解决删除数据效率比较低的问题。可以通过惰性删除有效避免Redis卡顿问题(大key删除等问题),步骤如下:

    • unlink key : 与DEL一样删除key功能的lazy free实现,唯一不同是,UNLINK在删除集合类型键时,如果集合键的元素个数大于64个,主线程中只是把待删除键从数据库字典中摘除,会把真正的内存释放操作,给单独的bio来操作。如果元素个数较少(少于64个)或者是String类型,也会在主线程中直接删除。

    • flushall/flushdb async : 对于清空数据库命令flushall/flushdb,添加了async异步清理选项,使得redis在清空数据库时都是异步操作。实现逻辑是为数据库新建一个新的空的字典,把原有旧的数据库字典给后台线程来逐一删除其中的数据,释放内存。

    • 把删除工作交给了后台子进程异步删除数据

    因为Redis是单个主线程处理,redis之父antirez一直强调"Lazy Redis is better Redis". 而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu时间片)较高删除操作, 从redis主线程剥离让bio子线程来处理,极大地减少主线阻塞时间。从而减少删除导致性能和稳定性问题。

    Redis 4.0 就引入了多个线程来实现数据的异步惰性删除等功能,但是其处理读写请求的仍然只有一个线程,所以仍然算是狭义上的单线程。

    从上一小结分析:Redis的主要性能瓶颈是内存或网络带宽而并非CPU。内存问题比较好解决,因此Redis的瓶颈原因为网络IO。接下来,引入多线程模型。

    3.2 多线程工作原理

    I/O 的读和写本身是堵塞的,比如当 socket 中有数据时,Redis 会通过调用先将数据从内核态空间拷贝到用户态空间,再交给 Redis 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。Une brève analyse des modèles monothread et multithread dans Redis6

    在 Redis 6.0 中新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),将最耗时的Socket的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。Une brève analyse des modèles monothread et multithread dans Redis6
    结合上图可知,将网络数据读写、请求协议解析通过多个IO线程的来处理,对于真正的命令执行来说,仍然使用主线程操作(线程安全),是个不错的折中办法。因此,对于整个Redis来说是多线程的,但是对于工作线程(命令执行)仍旧是单线程

    3.3 工作流程

    核心流程大概如下:Une brève analyse des modèles monothread et multithread dans Redis6
    流程简述如下:

    • 主线程获取 socket 放入等待列表
    • 将 socket 分配给各个 IO 线程(并不会等列表满)
    • 主线程阻塞等待 IO 线程(多线程)读取 socket 完毕
    • 主线程执行命令 - 单线程(如果命令没有接收完毕,会等 IO 下次继续)
    • 主线程阻塞等待 IO 线程(多线程)problème de suppression de clé importante
    •  : Dans des circonstances normales, l'instruction del peut être utilisée pour supprimer rapidement des données. Cependant, lorsque la clé supprimée est un objet très volumineux, tel qu'un ensemble de hachage contenant des milliers d'éléments, l'instruction del entraînera le blocage du fil principal Redis.
    • Par conséquent, un module multi-thread a été ajouté à
    Redis 4.0. Bien entendu, le multi-threading dans cette version vise principalement à résoudre le problème de la faible efficacité de suppression des données. Vous pouvez efficacement éviter les problèmes de décalage Redis (suppression de clé importante, etc.) grâce à la

    suppression paresseuse Les étapes sont les suivantes :

      🎜unlink key. : Une implémentation gratuite et paresseuse de la fonction de suppression de clé comme DEL. La seule différence est que lorsque UNLINK supprime une clé de type ensemble, si le nombre d'éléments dans la clé définie est supérieur à 64, le thread principal supprime uniquement le clé à supprimer du dictionnaire de la base de données. Laissez l’opération de libération de mémoire réelle dans une bio distincte. Si le nombre d'éléments est petit (inférieur à 64) ou de type String, ils seront également supprimés directement dans le thread principal. 🎜🎜🎜🎜flushall/flushdb async : Pour que la commande flushall/flushdb efface la base de données, l'option de nettoyage asynchrone asynchrone est ajoutée, afin que redis fonctionne de manière asynchrone lors de l'effacement de la base de données. La logique d'implémentation consiste à créer un nouveau dictionnaire vide pour la base de données et à donner l'ancien dictionnaire de base de données d'origine au thread d'arrière-plan pour supprimer les données une par une et libérer la mémoire. 🎜🎜🎜🎜Le travail de suppression est confié au processus enfant d'arrière-plan pour supprimer les données de manière asynchrone🎜🎜🎜🎜Parce que Redis est traité par un seul thread principal, antirez, le père de Redis, a toujours souligné que "Lazy Redis est meilleur Redis ". Et paresseux gratuit L'essence du code> est de supprimer certains coûts (réplication du temps principal, occupant la tranche de temps du processeur du thread principal) les opérations de suppression élevées du thread principal redis et de laisser le sous-bio -thread le gère, ce qui réduit considérablement le temps de blocage du thread principal. Cela réduit les problèmes de performances et de stabilité causés par la suppression. 🎜🎜Redis 4.0 a introduit plusieurs threads pour implémenter des fonctions telles que la suppression paresseuse asynchrone des données, mais il ne dispose toujours que d'un seul thread pour traiter les demandes de lecture et d'écriture, il est donc toujours considéré comme un seul thread au sens étroit. 🎜🎜 Analyse du résumé précédent : le principal goulot d'étranglement des performances de Redis est la mémoire ou la bande passante réseau plutôt que le processeur. Le problème de mémoire est relativement facile à résoudre, le goulot d'étranglement de Redis réside donc dans les E/S réseau. Ensuite, le modèle multi-thread est introduit. 🎜🎜🎜3.2 Principe de fonctionnement du multi-threading🎜🎜🎜La lecture et l'écriture des E/S elles-mêmes sont bloquées Par exemple, lorsqu'il y a des données dans le socket. , Redis Les données sont d'abord copiées de l'espace noyau vers l'espace utilisateur via l'appel, puis transmises à Redis pour l'appel. Ce processus de copie est bloquant. Lorsque la quantité de données est plus importante, la copie prend plus de temps. , et ces opérations sont terminées sur un seul thread. Une brève analyse des modèles monothread et multithread dans Redis6🎜🎜Dans Redis 6.0, une nouvelle fonction multi-threading a été ajoutée pour améliorer les performances de lecture et d'écriture des E/S🎜 Son idée principale d'implémentation est de diviser les tâches de lecture et d'écriture des E/S. fil principal Donnez un groupe de threads indépendants à exécuter, afin que la lecture et l'écriture de plusieurs sockets puissent être parallélisées. L'utilisation de la technologie de multiplexage d'E/S multicanal peut permettre à un seul thread de gérer efficacement plusieurs demandes de connexion (minimiser le réseau). Consommation de temps IO), la lecture de Socket, l'analyse des demandes et l'écriture les plus chronophages sont externalisées séparément, et l'exécution de la commande restante est toujours exécutée en série par le thread principal et interagit avec les données en mémoire. Une brève analyse des modèles monothread et multithread dans Redis6
      Comme le montre la figure ci-dessus, la lecture et l'écriture des données réseau ainsi que l'analyse du protocole de requête sont traitées via plusieurs threads IO. Pour l'exécution réelle des commandes, l'opération du thread principal est toujours utilisée. (Sécurité du fil)🎜, est un bon compromis. Par conséquent, est multithread pour l'ensemble de Redis, mais est toujours monothread pour le thread de travail (exécution de commande). 🎜

      3.3 Workflow

      🎜Le processus principal est le suivant : Une brève analyse des modèles monothread et multithread dans Redis6
      Le processus est brièvement décrit comme suit : 🎜🎜Maître Le thread obtient le socket et le met dans la liste d'attente🎜🎜Attribue le socket à chaque thread IO (il n'attendra pas que la liste soit pleine)🎜🎜Le thread principal Bloque et attend le Thread IO (multi-thread) La lecture du socket est terminée🎜🎜Commande d'exécution du thread principal - Thread unique (Si la commande n'est pas reçue, elle attendra que IO continue la prochaine fois) 🎜🎜Le thread principal Bloque et attend que le thread IO (multi-thread) réécrive les données Le socket est terminé (s'il n'est pas terminé une fois, il sera réécrit la prochaine fois)🎜🎜 Détachez et effacez la file d'attente🎜🎜🎜🎜Les fonctionnalités sont les suivantes :🎜🎜
      • Le thread IO lit ou écrit le socket en même temps, et ne lira ni n'écrira en même temps
      • Le thread IO est uniquement responsable de la lecture et de l'écriture des commandes d'analyse du socket, et n'est pas responsable de la commande. traitement (le thread principal exécute les commandes en série)
      • Le nombre de threads IO peut être auto-configuré (La limite supérieure actuelle de la limite de code est 512, et la valeur par défaut est 1 (désactivez cette fonction))

      Après des tests de résistance effectués par des personnes intéressées, les performances actuelles peuvent être augmentées de plus de 1 fois.

      Question 1 : La liste d'attente n'est pas pleine. Est-elle bloquée et non traitée ?
      Réponse : Une brève analyse des modèles monothread et multithread dans Redis6Ce qui est détecté lors du blocage, c'est si le thread IO a encore des tâches. Attendez que le traitement soit terminé avant de continuer. Ces tâches sont ajoutées lors de l'exécution. Si le nombre de tâches J'ai encore quelques doutes à ce sujet, quelqu'un peut-il l'expliquer (commentaires) ?

      3.4 Le multi-threading est-il activé par défaut ?

      Dans Redis6.0, Le mécanisme multi-threading est désactivé par défaut Si vous devez utiliser la fonction multi-threading, vous devez effectuer deux réglages dans redis.conf. Une brève analyse des modèles monothread et multithread dans Redis6多线程机制默认是关闭的 ,如果需要使用多线程功能,需要在redis.conf中完成两个设置。Une brève analyse des modèles monothread et multithread dans Redis6

      • 设置io-thread-do-reads配置项为yes,表示启动多线程。
      • 设置线程个数。关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3, 如果为 8 核 CPU 建议线程数设置为 6 ,线程数一定要小于机器核数,线程数并不是越大越好。

      4. 总结

      Redis自身出道就是优秀,基于内存操作、数据结构简单、多路复用和非阻塞 I/O、避免了不必要的线程上下文切换等特性,在单线程的环境下依然很快;

      但对于大数据的 key 删除还是卡顿厉害,因此在 Redis 4.0 引入了多线程unlink key/flushall async 等命令,主要用于 Redis 数据的异步删除;

      而在 Redis 6.0 中引入了 I/O 多线程的读写,这样就可以更加高效的处理更多的任务了, Redis 只是将 I/O 读写变成了多线程 ,而 命令的执行依旧是由主线程串行执行的 ,因此在多线程下操作 Redis 不会出现线程安全的问题

      Définissez l'élément de configuration io-thread-do-reads sur yes, ce qui signifie démarrer plusieurs threads. Définissez le nombre de fils. Concernant le paramétrage du nombre de threads, la recommandation officielle est que s'il s'agit d'un CPU 4 cœurs, il est recommandé de définir le nombre de threads sur 2 ou 3. S'il s'agit d'un CPU 8 cœurs, il est recommandé que le nombre de threads soit défini sur 6. Le nombre de threads doit être inférieur au nombre de cœurs de la machine, le nombre de threads n'est pas toujours meilleur.

      4. Résumé

      Redis lui-même est excellent depuis ses débuts, basé sur des opérations de mémoire, une structure de données simple, un multiplexage et des E/S non bloquantes, évitant les changements de contexte de thread inutiles et d'autres fonctionnalités, c'est toujours très rapide dans un environnement monothread ; 🎜🎜Mais la suppression des clés du Big Data est encore très lente, donc multi-thread unlink key/flushall async et d'autres commandes ont été introduites dans Redis 4.0, qui sont principalement utilisées pour les opérations asynchrones. suppression des données Redis ; 🎜🎜Dans Redis 6.0, la lecture et l'écriture multithread d'E/S ont été introduites, afin que davantage de tâches puissent être traitées plus efficacement. Redis transforme simplement la lecture et l'écriture d'E/S en multithreading<.>, et l'exécution des <code>commandes est toujours exécutée en série par le thread principal, il n'y aura donc aucun problème de sécurité des threads lors de l'utilisation de Redis sous multi-threads. 🎜🎜🎜Redis Qu'il s'agisse de la conception originale monothread ou de la conception multithread actuelle qui est contraire à la conception originale, il n'y a qu'un seul objectif : rendre Redis de plus en plus rapide. 🎜🎜🎜Pour plus de connaissances sur la programmation, veuillez visiter : 🎜Introduction à la 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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer