一文弄懂Redis單線程和多線程
Redis單線程
Redis為什么是單線程
Redis的版本很多,比如3.x、4.x、6.x等,版本不同,架構不同:
- 3.x版本,最早的版本,單線程
- 4.x版本,嚴格意義上來說不是單線程,負責處理客戶端請求的線程是單線程,并且加了一些多線程(比如:異步刪除)
- 2020年5月版本的6.0.x后及2022年出的7.0版本后,用一種全新的多線程來解決問題
介紹
Redis的單線程主要是指Redis網絡IO
和鍵值對讀寫
是由一個線程來完成的,Redis在處理客戶端的請求時包括獲取(Socket讀)、解析、執(zhí)行、內容返回(Socket寫)
等都是由一個順序串行的主線程處理,這時Redis對外提供鍵值對存儲服務的主要流程。
Redis其他功能,比如持久化RDB、AOF、異步刪除、集群數據同步等,是由額外的線程執(zhí)行的。
Redis命令的工作線程是單線程的,但是對于整個Redis來說,是多線程。
Redis演進
Redis 3.x 單線程時代性能很快的原因
基于內存操作
- 所有Redis的數據都存在內存中,因此所有的運算都是內存級別的,所以他的性能高
數據結構簡單
- Redis的數據結構是專門設計的,這些簡單的數據結構的查找和操作時間大部分復雜度都是O(1),性能高
多路復用和非阻塞IO
- Redis使用I/O多路復用功能來監(jiān)聽多個socket連接客戶端,這樣可以使用一個線程來處理多個請求,減少線程切換帶來額開銷,同時也避免了I/O阻塞操作
避免上下文切換
- 因為是單線程模型,因此就避免了不必要的上下文切換和多線程競爭,這就省去了多線程切換帶來的時間和性能上的消耗,而且單線程不會導致死鎖問題的發(fā)生
Redis4.0之前采用單線程的主要原因
使用單線程模型使Redis開發(fā)和維護更簡單,單線程模型方便開發(fā)和調試
使用單線程模式也能并發(fā)處理多客戶端的請求,主要使用IO多路復用和非阻塞IO
對于Redis來說,主要性能瓶頸是
內存和網絡帶寬
,不是CPU
Redis單線程添加多線程特性的原因
單線程問題:要刪除一個大key時,del bigkey
會一直阻塞,等待刪除完成,才能繼續(xù)操作,會導致Redis主線程卡頓
解決方法:引入了惰性刪除
有效避免Redis主線程卡頓。
lazy free的本質就是把某些cost(主要時間復制度,占用主線程cpu時間片)較高刪除操作,從redis主線程剝離讓BIO子線程來處理,極大地減少主線阻塞時間。從而減少刪除導致性能和穩(wěn)定性問題。
雖然引入了多個線程來實現(xiàn)數據的異步惰性刪除等功能,但其處理讀寫請求的仍然只有一個線程,所以仍然是狹義單線程。
Redis6/7多線程
Redis主要的性能瓶頸是內存和網絡帶寬,不是CPU
真正意義的多線程
? Redis一直被大家熟知的就是它的單線程架構,雖然有些命令操作可以用后臺線程或子進程執(zhí)行(比如數據刪除、快照生成、AOF重寫)。但是,從網絡IO處理到實際的讀寫命令處理,都是由單個線程完成的。?
隨著網絡硬件的性能提升,Redis的性能瓶頸有時會出現(xiàn)在網絡IO的處理上,也就是說,單個主線程處理網絡請求的速度跟不上底層網絡硬件的速度。
采用多個IO線程來處理網絡請求,提高網絡請求處理的并行度,Redis6/7就是采用的這種方法。?
但是,Redis的多IO線程只是用來處理網絡請求的,對于讀寫操作命令Redis仍然使用單線程來處理
。這是因為,Redis處理請求時,網絡處理經常是瓶頸,通過多個IO線程并行處理網絡操作,可以提升實例的整體處理性能。而繼續(xù)使用單線程執(zhí)行命令操作,就不用為了保證Lua腳本、事務的原子性,額外開發(fā)多線程互斥加鎖機制了(不管加鎖操作處理),這樣一來,Redis線程模型實現(xiàn)就簡單了。?
Redis 只是將 I/O 讀寫變成了多線程,而命令的執(zhí)行依舊是由主線程串行執(zhí)行的。
主線程和IO線程的四個階段
階段一
:服務端和客戶端建立Socket連接,并分配處理線程
首先,主線程負責接收建立連接請求,當有客戶端請求和實例建立Socket連接時,主線程會創(chuàng)建和客戶端的連接,并把Socket放入全局等待隊列中。緊接著,主線程通過輪詢方法把Socket連接分配給IO線程。
階段二
:IO線程讀取并解析請求
主線程一旦把Socket分配給IO線程,就會進入阻塞狀態(tài),等待IO線程完成客戶端請求讀取和解析。因為有多個IO線程在并行處理,所以該過程執(zhí)行很快。
階段三
:主線程執(zhí)行請求操作
等到IO線程解析完請求,主線程以單線程的方式執(zhí)行命令操作。
階段四
:IO線程回寫Socket和主線程清空全局隊列
當主線程執(zhí)行完請求操作后,把需要返回的結果寫入緩沖區(qū),然后主線程會阻塞等待IO線程,把這些結果回寫到Socket中,并返回給客戶端。和IO線程讀取和解析請求一樣,IO線程回寫Socket時,有多個IO線程在并行處理,所以該過程執(zhí)行很快,等到IO線程回寫Socket完畢,主線程會清空全局隊列,等待客戶端的后續(xù)請求。
從Redis6開始,新增了多線程的功能來提高IO的讀寫性能,主要實現(xiàn)思路是將主線程的IO讀寫任務拆分給一組獨立的線程去執(zhí)行,這樣就可以使用多個socket的讀寫進行并行化了,采用多路IO復用技術可以讓單個線程高效處理多個連接請求,將最耗時的socket的讀取、請求解析、寫入等單獨執(zhí)行,剩下的命令執(zhí)行仍然由主線程串行執(zhí)行并和內存的數據交換。
五種IO模型
IO多路復用
一種同步的IO模型,實現(xiàn)一個線程監(jiān)視多個文件句柄,一旦某個文件句柄就緒就能夠通知到對應應用程序進行相應的讀寫操作,沒有文件句柄就緒時就會阻塞應用程序,從而釋放CPU資源?!卷憫降摹?/p>
IO:網絡IO,尤其在操作系統(tǒng)中指數據在內核態(tài)和用戶態(tài)之間的讀寫操作
多路:多個客戶端連接(套接字描述符,Socket)
復用:復用一個或多個線程
注意:套接字描述符是訪問套接字的一種路徑,套接字對唯一標識一個網絡上的每個TCP連接。
IO多路復用: 一個或一組線程處理多個TCP連接,使用單進程就能夠實現(xiàn)同時處理多個客戶端的連接,無需創(chuàng)建或者維護過多的進程/線程。
- 一個服務端進程可以同時處理多個套接字描述符
- 實現(xiàn)IO多路復用的模型有3種: select -> poll -> epoll 三個階段
只使用一個服務端進程可以同時處理多個套接字描述符連接。
Redis快的原因:IO多路復用+epoll函數的使用。
Redis7開啟多線程
在單機模式下,可以開啟多線程,但是在其他模式,最好不開啟
Redis實例的 CPU開銷不大但吞吐量卻沒有提升,可以考慮使用Redis7的多線程機制,加速網絡處理,進而提升實例的吞吐量。
io-threads 4 io-threads-do-redis no
注意線程數
- 官方的建議是如果為 4 核的 CPU,建議線程數設置為 2 或 3,如果為 8 核 CPU 建議線程數設置為 6,線程數一定要小于機器核數,線程數并不是越大越好。
設置io-thread-do-reads配置項為yes,表示啟動多線程。
到此這篇關于一文弄懂Redis單線程和多線程的文章就介紹到這了,更多相關Redis單線程和多線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何使用?redis?消息隊列完成秒殺過期訂單處理操作(二)
這篇文章主要介紹了如何使用?redis?消息隊列完成秒殺過期訂單處理操作,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-07-07詳解Redis中的BigKey如何發(fā)現(xiàn)和處理
這篇文章主要為大家詳細介紹了Redis中的BigKey如何發(fā)現(xiàn)和處理,文中給大家詳細講解了BigKey危害和如何解決這些問題,文章通過代碼示例和圖文介紹的非常詳細,需要的朋友可以參考下2023-10-10Redis中l(wèi)ua腳本實現(xiàn)及其應用場景
本文主要介紹了Redis中l(wèi)ua腳本實現(xiàn)及其應用場景,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04