Maison > Article > base de données > Une brève analyse des modèles monothread et multithread dans Redis6
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 !
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]
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.1 单线程真实含义
主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。这也是Redis对外提供键值存储服务的主要流程。
但Redis的其他功能, 比如持久化、异步删除、集群数据同步等等,其实是由额外的线程
执行的。
可以这么说,Redis工作线程是单线程的。但是,整个Redis来说,是多线程的
;
2.2 单线程性能快原因
Redis 3.x 单线程时代但是性能很快的主要原因
:
o(1)
2.3 采用单线程原因
Redis 是基于内存操作的, 因此他的瓶颈可能是机器的内存或者网络带宽而并非 CPU
,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了,况且使用多线程比较麻烦。 但是在 Redis 4.0 中开始支持多线程了,例如后台删除等功能
。
简单来说,Redis 4.0 之前一直采用单线程的主要原因有以下三个:
使用单线程模型是 Redis 的开发和维护更简单,因为单线程模型方便开发和调试;多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,因此没有必要使用多线程。单线程机制使得 Redis 内部实现的复杂度大大降低,Hash 的惰性 Rehash、Lpush 等等 “线程不安全” 的命令都可以无锁进行。
即使使用单线程模型也并发的处理多客户端的请求,主要使用的是多路复用和非阻塞 IO;
对于 Redis 系统来说, 主要的性能瓶颈是内存或者网络带宽而并非 CPU
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.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 :🎜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
. 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 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。
在 Redis 6.0 中新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),将最耗时的Socket的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。
结合上图可知,将网络数据读写、请求协议解析通过多个IO线程的来处理,对于真正的命令执行来说,仍然使用主线程操作(线程安全),是个不错的折中办法。因此,对于整个Redis来说是多线程的,但是对于工作线程(命令执行)仍旧是单线程
。
核心流程大概如下:
流程简述如下:
阻塞等待 IO 线程(多线程)
读取 socket 完毕单线程
(如果命令没有接收完毕,会等 IO 下次继续)阻塞等待 IO 线程(多线程)
problème de suppression de clé importantesuppression 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. 🎜🎜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. est multithread pour l'ensemble de Redis, mais est toujours monothread pour le thread de travail (exécution de commande)
. 🎜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 :🎜🎜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 : Ce 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. 多线程机制默认是关闭的
,如果需要使用多线程功能,需要在redis.conf中完成两个设置。
如果为 8 核 CPU 建议线程数设置为 6
,线程数一定要小于机器核数,线程数并不是越大越好。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.
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!