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