一文弄懂Redis單線程和多線程
Redis單線程
Redis為什么是單線程
Redis的版本很多,比如3.x、4.x、6.x等,版本不同,架構(gòu)不同:
- 3.x版本,最早的版本,單線程
- 4.x版本,嚴(yán)格意義上來說不是單線程,負(fù)責(zé)處理客戶端請求的線程是單線程,并且加了一些多線程(比如:異步刪除)
- 2020年5月版本的6.0.x后及2022年出的7.0版本后,用一種全新的多線程來解決問題
介紹
Redis的單線程主要是指Redis網(wǎng)絡(luò)IO和鍵值對讀寫是由一個線程來完成的,Redis在處理客戶端的請求時包括獲取(Socket讀)、解析、執(zhí)行、內(nèi)容返回(Socket寫)等都是由一個順序串行的主線程處理,這時Redis對外提供鍵值對存儲服務(wù)的主要流程。

Redis其他功能,比如持久化RDB、AOF、異步刪除、集群數(shù)據(jù)同步等,是由額外的線程執(zhí)行的。
Redis命令的工作線程是單線程的,但是對于整個Redis來說,是多線程。
Redis演進(jìn)
Redis 3.x 單線程時代性能很快的原因
基于內(nèi)存操作
- 所有Redis的數(shù)據(jù)都存在內(nèi)存中,因此所有的運算都是內(nèi)存級別的,所以他的性能高
數(shù)據(jù)結(jié)構(gòu)簡單
- Redis的數(shù)據(jù)結(jié)構(gòu)是專門設(shè)計的,這些簡單的數(shù)據(jù)結(jié)構(gòu)的查找和操作時間大部分復(fù)雜度都是O(1),性能高
多路復(fù)用和非阻塞IO
- Redis使用I/O多路復(fù)用功能來監(jiān)聽多個socket連接客戶端,這樣可以使用一個線程來處理多個請求,減少線程切換帶來額開銷,同時也避免了I/O阻塞操作
避免上下文切換
- 因為是單線程模型,因此就避免了不必要的上下文切換和多線程競爭,這就省去了多線程切換帶來的時間和性能上的消耗,而且單線程不會導(dǎo)致死鎖問題的發(fā)生
Redis4.0之前采用單線程的主要原因
使用單線程模型使Redis開發(fā)和維護(hù)更簡單,單線程模型方便開發(fā)和調(diào)試
使用單線程模式也能并發(fā)處理多客戶端的請求,主要使用IO多路復(fù)用和非阻塞IO
對于Redis來說,主要性能瓶頸是
內(nèi)存和網(wǎng)絡(luò)帶寬,不是CPU
Redis單線程添加多線程特性的原因
單線程問題:要刪除一個大key時,del bigkey會一直阻塞,等待刪除完成,才能繼續(xù)操作,會導(dǎo)致Redis主線程卡頓
解決方法:引入了惰性刪除有效避免Redis主線程卡頓。
lazy free的本質(zhì)就是把某些cost(主要時間復(fù)制度,占用主線程cpu時間片)較高刪除操作,從redis主線程剝離讓BIO子線程來處理,極大地減少主線阻塞時間。從而減少刪除導(dǎo)致性能和穩(wěn)定性問題。
雖然引入了多個線程來實現(xiàn)數(shù)據(jù)的異步惰性刪除等功能,但其處理讀寫請求的仍然只有一個線程,所以仍然是狹義單線程。
Redis6/7多線程
Redis主要的性能瓶頸是內(nèi)存和網(wǎng)絡(luò)帶寬,不是CPU
真正意義的多線程
? Redis一直被大家熟知的就是它的單線程架構(gòu),雖然有些命令操作可以用后臺線程或子進(jìn)程執(zhí)行(比如數(shù)據(jù)刪除、快照生成、AOF重寫)。但是,從網(wǎng)絡(luò)IO處理到實際的讀寫命令處理,都是由單個線程完成的。?
隨著網(wǎng)絡(luò)硬件的性能提升,Redis的性能瓶頸有時會出現(xiàn)在網(wǎng)絡(luò)IO的處理上,也就是說,單個主線程處理網(wǎng)絡(luò)請求的速度跟不上底層網(wǎng)絡(luò)硬件的速度。采用多個IO線程來處理網(wǎng)絡(luò)請求,提高網(wǎng)絡(luò)請求處理的并行度,Redis6/7就是采用的這種方法。?
但是,Redis的多IO線程只是用來處理網(wǎng)絡(luò)請求的,對于讀寫操作命令Redis仍然使用單線程來處理。這是因為,Redis處理請求時,網(wǎng)絡(luò)處理經(jīng)常是瓶頸,通過多個IO線程并行處理網(wǎng)絡(luò)操作,可以提升實例的整體處理性能。而繼續(xù)使用單線程執(zhí)行命令操作,就不用為了保證Lua腳本、事務(wù)的原子性,額外開發(fā)多線程互斥加鎖機(jī)制了(不管加鎖操作處理),這樣一來,Redis線程模型實現(xiàn)就簡單了。?
Redis 只是將 I/O 讀寫變成了多線程,而命令的執(zhí)行依舊是由主線程串行執(zhí)行的。
主線程和IO線程的四個階段
階段一:服務(wù)端和客戶端建立Socket連接,并分配處理線程
首先,主線程負(fù)責(zé)接收建立連接請求,當(dāng)有客戶端請求和實例建立Socket連接時,主線程會創(chuàng)建和客戶端的連接,并把Socket放入全局等待隊列中。緊接著,主線程通過輪詢方法把Socket連接分配給IO線程。
階段二:IO線程讀取并解析請求
主線程一旦把Socket分配給IO線程,就會進(jìn)入阻塞狀態(tài),等待IO線程完成客戶端請求讀取和解析。因為有多個IO線程在并行處理,所以該過程執(zhí)行很快。
階段三:主線程執(zhí)行請求操作
等到IO線程解析完請求,主線程以單線程的方式執(zhí)行命令操作。
階段四:IO線程回寫Socket和主線程清空全局隊列
當(dāng)主線程執(zhí)行完請求操作后,把需要返回的結(jié)果寫入緩沖區(qū),然后主線程會阻塞等待IO線程,把這些結(jié)果回寫到Socket中,并返回給客戶端。和IO線程讀取和解析請求一樣,IO線程回寫Socket時,有多個IO線程在并行處理,所以該過程執(zhí)行很快,等到IO線程回寫Socket完畢,主線程會清空全局隊列,等待客戶端的后續(xù)請求。


從Redis6開始,新增了多線程的功能來提高IO的讀寫性能,主要實現(xiàn)思路是將主線程的IO讀寫任務(wù)拆分給一組獨立的線程去執(zhí)行,這樣就可以使用多個socket的讀寫進(jìn)行并行化了,采用多路IO復(fù)用技術(shù)可以讓單個線程高效處理多個連接請求,將最耗時的socket的讀取、請求解析、寫入等單獨執(zhí)行,剩下的命令執(zhí)行仍然由主線程串行執(zhí)行并和內(nèi)存的數(shù)據(jù)交換。

五種IO模型
IO多路復(fù)用
一種同步的IO模型,實現(xiàn)一個線程監(jiān)視多個文件句柄,一旦某個文件句柄就緒就能夠通知到對應(yīng)應(yīng)用程序進(jìn)行相應(yīng)的讀寫操作,沒有文件句柄就緒時就會阻塞應(yīng)用程序,從而釋放CPU資源?!卷憫?yīng)式的】
IO:網(wǎng)絡(luò)IO,尤其在操作系統(tǒng)中指數(shù)據(jù)在內(nèi)核態(tài)和用戶態(tài)之間的讀寫操作
多路:多個客戶端連接(套接字描述符,Socket)
復(fù)用:復(fù)用一個或多個線程
注意:套接字描述符是訪問套接字的一種路徑,套接字對唯一標(biāo)識一個網(wǎng)絡(luò)上的每個TCP連接。
IO多路復(fù)用: 一個或一組線程處理多個TCP連接,使用單進(jìn)程就能夠?qū)崿F(xiàn)同時處理多個客戶端的連接,無需創(chuàng)建或者維護(hù)過多的進(jìn)程/線程。
- 一個服務(wù)端進(jìn)程可以同時處理多個套接字描述符
- 實現(xiàn)IO多路復(fù)用的模型有3種: select -> poll -> epoll 三個階段

只使用一個服務(wù)端進(jìn)程可以同時處理多個套接字描述符連接。

Redis快的原因:IO多路復(fù)用+epoll函數(shù)的使用。
Redis7開啟多線程
在單機(jī)模式下,可以開啟多線程,但是在其他模式,最好不開啟
Redis實例的 CPU開銷不大但吞吐量卻沒有提升,可以考慮使用Redis7的多線程機(jī)制,加速網(wǎng)絡(luò)處理,進(jìn)而提升實例的吞吐量。
io-threads 4 io-threads-do-redis no
注意線程數(shù)
- 官方的建議是如果為 4 核的 CPU,建議線程數(shù)設(shè)置為 2 或 3,如果為 8 核 CPU 建議線程數(shù)設(shè)置為 6,線程數(shù)一定要小于機(jī)器核數(shù),線程數(shù)并不是越大越好。
設(shè)置io-thread-do-reads配置項為yes,表示啟動多線程。
到此這篇關(guān)于一文弄懂Redis單線程和多線程的文章就介紹到這了,更多相關(guān)Redis單線程和多線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于使用IDEA的springboot框架往Redis里寫入數(shù)據(jù)亂碼問題
這篇文章主要介紹了用IDEA的springboot框架往Redis里寫入數(shù)據(jù)亂碼問題,本文給大家分享解決方法通過圖文示例相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
Redis利用I/O多路復(fù)用實現(xiàn)高并發(fā)
本文主要介紹了Redis利用I/O多路復(fù)用實現(xiàn)高并發(fā),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-12-12
使用JMeter插件Redis Data Set如何實現(xiàn)高性能數(shù)據(jù)驅(qū)動測試
RedisDataSet插件是JMeter的一個插件,可以實現(xiàn)從Redis中動態(tài)加載數(shù)據(jù),并將其用作測試參數(shù),本文詳細(xì)介紹如何在JMeter中使用RedisDataSet插件,幫助你實現(xiàn)高效的數(shù)據(jù)驅(qū)動測試2025-01-01
Redis從基礎(chǔ)到Stream消息隊列實戰(zhàn)指南
Redis是一個高性能內(nèi)存數(shù)據(jù)庫,支持字符串、哈希、列表等數(shù)據(jù)結(jié)構(gòu),5.0版本新增Stream類型,提供消息隊列功能,支持消費者組和消息確認(rèn),本文介紹Redis從基礎(chǔ)到Stream消息隊列實戰(zhàn)指南,感興趣的朋友跟隨小編一起看看吧2026-03-03
詳解redis大幅性能提升之使用管道(PipeLine)和批量(Batch)操作
這篇文章主要介紹了詳解redis大幅性能提升之使用管道(PipeLine)和批量(Batch)操作 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12
基于Redis Streams的實時消息處理實戰(zhàn)指南
這篇文章主要為大家詳細(xì)介紹了在生產(chǎn)環(huán)境中基于 Redis Streams 構(gòu)建實時消息處理的完整經(jīng)驗,包括技術(shù)選型、核心代碼示例、踩坑解決和優(yōu)化方案,希望對大家有所幫助2025-07-07

