Dieser Artikel führt Sie zum Verständnis des Thread-Modells in Redis6 und stellt das Single-Thread-Modell und das Multi-Thread-Modell vor. Ich hoffe, dass er Ihnen hilfreich sein wird!
Wenn wir einfach sagen, dass Redis Single-Threaded oder Multi-Threaded ist, ist diese Antwort sicherlich nicht streng. Die von verschiedenen Versionen verwendeten Threading-Modelle sind unterschiedlich. [Verwandte Empfehlungen: Redis-Video-Tutorial]
Version 3.x, die früheste Version, bei der es sich um Mundpropaganda handelt, ist Single-Threaded.
Version 4.x ist streng genommen kein Single-Thread, aber der Thread, der für die Verarbeitung von Client-Anfragen verantwortlich ist, ist Single-Thread, hat aber begonnen, einige Multi-Thread-Dinge (asynchrones Löschen) . <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
zusätzlichen Threads
ausgeführt.
Man kann sagen, dass der Redis-Worker-Thread ein Single-Thread ist. Allerdings ist das gesamte Redis multi-threaded
; Code>Leistung ist schnell:Einfache Datenstruktur: Die Datenstruktur von Redis ist speziell entwickelt und einfach Daten Die meiste Zeit ist die Komplexität der Struktursuche und -operation o(1)
Multiplexen und Blockieren von IO: Verwenden Sie die IO-Multiplexing-Funktion, um mehrere Socket-Verbindungen zum Client zu überwachen. Auf diese Weise ein Thread Die Verbindung kann zum Verarbeiten mehrerer Anforderungen verwendet werden, wodurch der durch Thread-Wechsel verursachte Overhead reduziert und gleichzeitig E/A-Blockierungsvorgänge vermieden werden.
Multithreading wie Hintergrundlöschung und andere Funktionen
zu unterstützen. Da Single-Threading so gut ist, warum müssen wir Multi-Threading einführen?
Ein einzelner Thread hat auch seine eigenen Probleme, wie zum Beispiel das 既然单线程那么好,为啥又要引入多线程?
单线程也有自己的烦恼,比如大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 线程(多线程)
Problem beim Löschen großer SchlüsselLazy Deletion effektiv vermeiden. Die Schritte sind wie folgt:
unlink key
: Eine verzögerte, kostenlose Implementierung der Schlüssellöschfunktion wie DEL. Der einzige Unterschied besteht darin, dass der Hauptthread beim Löschen eines festgelegten Typschlüssels nur diesen entfernt, wenn die Anzahl der Elemente im festgelegten Schlüssel größer als 64 ist Schlüssel, der aus dem Datenbankwörterbuch gelöscht werden soll. Überlassen Sie den eigentlichen Speicherfreigabevorgang einer separaten Biografie. Wenn die Anzahl der Elemente gering ist (weniger als 64) oder vom Typ String, werden diese auch direkt im Hauptthread gelöscht. 🎜🎜🎜🎜flushall/flushdb async
: Für den Befehl „flushall/flushdb“ zum Löschen der Datenbank wird die Option „asynchrone asynchrone Bereinigung“ hinzugefügt, sodass Redis beim Löschen der Datenbank asynchron arbeitet. Die Implementierungslogik besteht darin, ein neues leeres Wörterbuch für die Datenbank zu erstellen und das ursprüngliche alte Datenbankwörterbuch an den Hintergrundthread zu übergeben, um die Daten einzeln zu löschen und den Speicher freizugeben. 🎜🎜🎜🎜Die Löscharbeit wird an den untergeordneten Hintergrundprozess übergeben, um Daten asynchron zu löschen🎜🎜🎜🎜Da Redis von einem einzigen Hauptthread verarbeitet wird, hat Antirez, der Vater von Redis, immer betont: „Lazy Redis ist das bessere Redis.“ ". Und Lazy Free Die Essenz des Codes> besteht darin, bestimmte kostspielige (Hauptzeitreplikation, Belegung der CPU-Zeitscheibe des Hauptthreads) und hohe Löschvorgänge
aus dem Redis-Hauptthread zu entfernen und den Bio-Sub zu lassen -thread verarbeitet es, was die Blockierungszeit des Hauptthreads erheblich verkürzt. Dies reduziert Leistungs- und Stabilitätsprobleme, die durch das Löschen verursacht werden. 🎜🎜Redis 4.0 führte mehrere Threads ein, um Funktionen wie das asynchrone verzögerte Löschen von Daten zu implementieren, verfügt jedoch immer noch nur über einen Thread zum Verarbeiten von Lese- und Schreibanforderungen, sodass es im engeren Sinne immer noch als einzelner Thread betrachtet wird. 🎜🎜 Analyse aus der vorherigen Zusammenfassung: Der Hauptleistungsengpass von Redis ist die Speicher- oder Netzwerkbandbreite und nicht die CPU. Das Speicherproblem ist relativ einfach zu lösen, daher ist der Engpass von Redis die Netzwerk-E/A. Als nächstes wird das Multithreading-Modell vorgestellt. 🎜🎜🎜3.2 Funktionsprinzip von Multithreading🎜🎜🎜E/A-Lesen und -Schreiben selbst wird blockiert, wenn sich beispielsweise Daten im Socket befinden , Redis wird die Daten zunächst durch den Aufruf aus dem Kernel-Bereich in den Benutzerbereich kopieren und dann für den Aufruf an Redis übergeben. Dieser Kopiervorgang ist blockierend, wenn die Datenmenge größer ist , und diese Vorgänge werden in einem einzelnen Thread abgeschlossen. 🎜🎜In Redis 6.0 wurde eine neue Multithreading-Funktion hinzugefügt, um die E/A-Lese- und Schreibleistung zu verbessern. Die Hauptidee der Implementierung besteht darin, die E/A-Lese- und Schreibaufgaben aufzuteilen Hauptthread Geben Sie eine Gruppe unabhängiger Threads zur Ausführung an, sodass das Lesen und Schreiben mehrerer Sockets parallelisiert werden kann. Durch die Verwendung der Mehrkanal-E/A-Multiplexing-Technologie kann ein einzelner Thread mehrere Verbindungsanforderungen effizient verarbeiten (Minimierung des Netzwerks). IO-Zeitverbrauch), das zeitaufwändigste Lesen von Sockets, das Parsen von Anforderungen und das Schreiben werden separat ausgelagert, und die verbleibende Befehlsausführung wird weiterhin seriell vom Hauptthread ausgeführt und interagiert mit den Daten im Speicher. multithreaded für das gesamte Redis, aber immer noch singlethreaded für den Arbeitsthread (Befehlsausführung)
. 🎜Nach Belastungstests durch Interessierte kann die aktuelle Leistungsfähigkeit um mehr als das 1-fache gesteigert werden.
Frage 1: Die Warteliste ist nicht voll und wird nicht bearbeitet.
Antwort: Beim Blockieren wird erkannt, ob der IO-Thread noch Aufgaben hat. Warten Sie, bis die Verarbeitung abgeschlossen ist, bevor Sie fortfahren. Diese Aufgaben werden während der Ausführung hinzugefügt. Wenn die Anzahl der Aufgaben Ich habe immer noch Zweifel daran. Kann mir das jemand erklären (Kommentar)?
3.4 Ist Multithreading standardmäßig aktiviert?
In Redis6.0 ist Der Multithreading-Mechanismus standardmäßig deaktiviert
. Wenn Sie die Multithreading-Funktion verwenden müssen, müssen Sie zwei Einstellungen in redis.conf vornehmen. 多线程机制默认是关闭的
,如果需要使用多线程功能,需要在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 不会出现线程安全的问题
Setzen Sie das Konfigurationselement io-thread-do-reads auf „Ja“, was bedeutet, dass mehrere Threads gestartet werden. Legen Sie die Anzahl der Threads fest. In Bezug auf die Einstellung der Anzahl der Threads lautet die offizielle Empfehlung: Wenn es sich um eine 4-Kern-CPU handelt, wird empfohlen, die Anzahl der Threads auf 2 oder 3 festzulegen. Wenn es sich um eine 8-Kern-CPU handelt, Es wird empfohlen, die Anzahl der Threads auf 6 festzulegen. Die Anzahl der Threads muss geringer sein als die Anzahl der Maschinenkerne. Die Anzahl der Threads ist nicht immer besser.
Befehlen wird weiterhin seriell vom Hauptthread
ausgeführt, sodass beim Betrieb von Redis unter Multithreads keine Thread-Sicherheitsprobleme auftreten
. 🎜🎜🎜Redis Egal, ob es sich um das ursprüngliche Single-Thread-Design oder das aktuelle Multi-Thread-Design handelt, das dem ursprünglichen Design widerspricht, es gibt nur einen Zweck: Redis immer schneller zu machen. 🎜🎜🎜Weitere Kenntnisse zum Thema Programmierung finden Sie unter: 🎜Einführung in die Programmierung🎜! ! 🎜Das obige ist der detaillierte Inhalt vonEine kurze Analyse von Single-Thread- und Multi-Thread-Modellen in Redis6. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!