S'il n'y a pas de bonne conception du système, l'utilisation du multi-threading conduira généralement aux résultats affichés à droite (notez l'ordonnée ). Lorsque vous augmentez pour la première fois le nombre de threads, le débit du système augmente. Lorsque vous augmentez encore le nombre de threads, le débit du système augmente lentement, voire diminue.
Le principal goulot d'étranglement est le suivant : il existe généralement des ressources partagées auxquelles plusieurs threads accèdent en même temps dans le système afin de garantir l'exactitude. des ressources partagées, il est nécessaire de disposer de mécanismes supplémentaires pour garantir la sécurité des threads, tels que le verrouillage, qui entraîneront une surcharge supplémentaire.
Prenons l'exemple du type List
le plus couramment utilisé. Supposons que Redis adopte une conception multithread et qu'il existe deux threads A et B qui gèrent List.
respectivement pour effectuer les opérations LPUSH
et LPUSH
, afin d'obtenir le même résultat à chaque fois, c'est-à-dire que [le thread B retire les données mises par A. thread], ces deux processus doivent être exécutés en série. Il s’agit du problème de contrôle d’accès simultané aux ressources partagées auquel est confronté le modèle de programmation multithread. List
类型来举例吧,假设Redis采用多线程设计,有两个线程A和B分别对List
做LPUSH
和LPUSH
操作,为了使得每次执行都是相同的结果,即【B线程取出A线程放入的数据】就需要让这两个过程串行执行。这就是多线程编程模式面临的共享资源的并发访问控制问题。
并发访问控制一直是多线程开发中的一个难点问题:如果只是简单地采用一个互斥锁,就会出现即使增加了线程,大部分线程也在等待获取互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。
同时加入并发访问控制后也会降低系统代码的可读性和可维护性,所以Redis干脆直接采用了单线程模式。
之所以使用单线程是Redis设计者多方面衡量的结果。
Redis的大部分操作在内存上完成
采用了高效的数据结构,例如哈希表和跳表
采用了多路复用机制,使其在网络IO操作中能并发处理大量的客户端请求,实现高吞吐率
既然Redis使用单线程进行IO,如果线程被阻塞了就无法进行多路复用了,所以不难想象,Redis肯定还针对网络和IO操作的潜在阻塞点进行了设计。
在网络通信里,服务器为了处理一个Get请求,需要监听客户端请求(bind/listen
),和客户端建立连接(accept
),从socket中读取请求(recv
),解析客户端发送请求(parse
),最后给客户端返回结果(send
)。
最基本的一种单线程实现是依次执行上面的操作。
上面标红的accept和recv操作都是潜在的阻塞点:
当Redis监听到有连接请求,但却一直不能成功建立起连接时,就会阻塞在accept()
函数这里,其他客户端此时也无法和Redis建立连接
当Redis通过recv()
从一个客户端读取数据时,如果数据一直没有到达,也会一直阻塞
为了解决IO中的阻塞问题,Redis采用了Linux的IO多路复用机制,该机制允许内核中,同时存在多个监听套接字和已连接套接字(select/epoll
)。
内核会一直监听这些套接字上的连接或数据请求。Redis会处理到达的请求,从而实现了一个线程处理多个IO流的效果。
此时,Redis线程就不会阻塞在某一个特定的客户端请求处理上,所以它可以同时和多个客户端连接并处理请求。
select/epoll一旦监测到FD上有请求到达时,就会触发相应的事件被放进一个队列里,Redis线程对该事件队列不断进行处理,所以就实现了基于事件的回调。
例如,Redis会对Accept和Read事件注册accept
和get
回调函数。当Linux内核监听到有连接请求或读数据请求时,就会触发Accept事件和Read事件,此时,内核就会回调Redis相应的accept
和get
bind /listen code>), établir une connexion avec le client (<code>accept
), lire la requête depuis le socket (recv
) et analyser la requête envoyée par le client (parse
>), et renvoie enfin le résultat au client (send
). #🎜🎜##🎜🎜#L'implémentation monothread la plus basique consiste à effectuer les opérations ci-dessus dans l'ordre. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Les opérations d'acceptation et de réception marquées en rouge ci-dessus sont des points de blocage potentiels : #🎜🎜#accept()
et les autres clients ne pourront pas établir de connexion avec Redis pour le moment #🎜🎜 #recv()
, si les données ne sont pas arrivées, il bloquera toujours #🎜🎜#select/epoll
). #🎜🎜##🎜🎜#Le noyau écoutera toujours les connexions ou les demandes de données sur ces sockets. Redis traitera les requêtes entrantes, obtenant ainsi l'effet d'un thread traitant plusieurs flux d'E/S. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Pour le moment, le thread Redis ne sera pas bloqué sur le traitement d'une demande client spécifique, il peut donc se connecter à plusieurs clients en même temps et traiter les demandes. #🎜🎜##🎜🎜#Mécanisme de rappel#🎜🎜##🎜🎜#select/epoll Une fois qu'une requête arrive sur le FD, l'événement correspondant sera déclenché et mis en file d'attente, et le thread Redis mettra l'événement en file d'attente. Traitement se poursuit, de sorte que les rappels basés sur les événements sont implémentés. #🎜🎜##🎜🎜#Par exemple, Redis enregistrera les fonctions de rappel accept
et get
pour les événements Accept et Read. Lorsque le noyau Linux surveille une demande de connexion ou une demande de lecture de données, il déclenchera l'événement Accept et l'événement Read. À ce moment, le noyau rappellera les accept
et get correspondants. code> de la fonction Redis à traiter. #🎜🎜##🎜🎜#Les goulots d'étranglement des performances de Redis#🎜🎜##🎜🎜#Après l'analyse ci-dessus, bien que plusieurs demandes de clients puissent être surveillées en même temps via le mécanisme de multiplexage, Redis présente encore des goulots d'étranglement de performances , c'est aussi une situation que nous devons éviter dans notre programmation quotidienne. #🎜🎜#<h4>1. Opérations chronophages</h4>
<p>Si une requête prend beaucoup de temps dans Redis, cela aura un impact sur les performances de l'ensemble du serveur. Les demandes suivantes doivent attendre que la demande précédente, qui prend beaucoup de temps, soit traitée avant de pouvoir être traitées. </p>
<p>Cela doit être évité lors de la conception de scénarios commerciaux ; le mécanisme <code>lazy-free
de Redis place également l'opération fastidieuse de libération de mémoire dans un thread asynchrone pour l'exécution.
Lorsque le degré de concurrence est très important, il existe un goulot d'étranglement dans les performances de lecture et d'écriture des données d'E/S client avec un seul thread. Bien que le mécanisme de multiplexage d'E/S soit utilisé, il ne peut toujours lire que le client. données séquentiellement avec un seul thread et ne peut pas tirer parti de plusieurs cœurs de processeur.
Redis dans 6.0 peut utiliser le processeur multicœur et multithread pour lire et écrire les données client, mais seules la lecture et l'écriture pour le client sont parallèles et le fonctionnement réel de chaque commande est toujours monothread.
J'aimerais profiter de cette occasion pour poser quelques questions intéressantes liées à Redis.
Pourquoi utiliser Redis N'est-ce pas mauvais d'accéder directement à la mémoire ?
En fait, cet article n'est pas clairement défini. Pour certaines données qui ne changent pas fréquemment, elles peuvent être placées directement dans la mémoire. Il peut y avoir des problèmes de cohérence lors de la mise à jour des données, c'est-à-dire que les données d'un seul serveur peuvent être modifiées, de sorte que les données n'existent que dans la mémoire locale. L'accès au serveur Redis peut résoudre le problème de cohérence, en utilisant Redis.
Que dois-je faire s'il y a trop de données qui ne peuvent pas être stockées dans la mémoire ? Par exemple, si je souhaite mettre en cache 100 Go de données, que dois-je faire ?
Il y a aussi une publicité ici. Tair est le système de cache KV distribué open source de Taobao. Il hérite des opérations riches de Redis. Théoriquement, le volume total de données est illimité. Mettez à niveau, les amis intéressés peuvent le découvrir ~
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!