Redis為什么又引入了多線程?單線程不香了?
相信你一定不止一次見(jiàn)過(guò)Redis是單線程模式,不過(guò)說(shuō)實(shí)話那只是個(gè)老版本,這個(gè)問(wèn)題是一位老哥的大廠面試題,跟我分享了一下。想著自己就知道redis6.0以前一直都是單線程,到了6的版本才加入了多線程,還不是很清楚,在多方打聽(tīng)并且搜索之下總結(jié)了這篇文章。
一、問(wèn)題概述
Redis 6.0 之后的版本拋棄了單線程模型這一設(shè)計(jì),原本使用單線程運(yùn)行的 Redis 也開(kāi)始選擇性使用多線程模型,乍一看Redis的作者這么牛,也逃不過(guò)“真香定律”,
仔細(xì)想想,這個(gè)問(wèn)題其實(shí)可以拆分,拆分為兩個(gè)主要的問(wèn)題:
(1)為什么 Redis 一開(kāi)始選擇單線程模型(單線程的好處)?
(2)為什么 Redis 在 6.0 之后加入了多線程(在某些情況下,單線程出現(xiàn)了缺點(diǎn),多線程可以解決)?
其實(shí),作者并不是沒(méi)有逃脫真香定理,而是隨著時(shí)間的推移,出現(xiàn)的問(wèn)題也越來(lái)越多,原來(lái)的設(shè)計(jì)肯定就有些不合時(shí)宜,該做出改變就做出改變。OK,帶著倆問(wèn)題,我們就來(lái)好好地分析一下。
二、為什么Redis一開(kāi)始使用單線程
不管是單線程或者是多線程都是為了提升Redis的開(kāi)發(fā)效率,因?yàn)镽edis是一個(gè)基于內(nèi)存的數(shù)據(jù)庫(kù),還要處理大量的外部的網(wǎng)絡(luò)請(qǐng)求,這就不可避免的要進(jìn)行多次IO。好在Redis使用了很多優(yōu)秀的機(jī)制來(lái)保證了它的高效率。那么為什么Redis要設(shè)計(jì)成單線程模式的呢?可以總結(jié)如下:
(1)IO多路復(fù)用
我們來(lái)看一下Redis頂層設(shè)計(jì)。
FD是一個(gè)文件描述符,意思是表示當(dāng)前文件處于可讀、可寫(xiě)還是異常狀態(tài)。使用 I/O 多路復(fù)用機(jī)制同時(shí)監(jiān)聽(tīng)多個(gè)文件描述符的可讀和可寫(xiě)狀態(tài)。你可以理解為具有了多線程的特點(diǎn)。
一旦受到網(wǎng)絡(luò)請(qǐng)求就會(huì)在內(nèi)存中快速處理,由于絕大多數(shù)的操作都是純內(nèi)存的,所以處理的速度會(huì)非常地快。也就是說(shuō)在單線程模式下,即使連接的網(wǎng)絡(luò)處理很多,因?yàn)橛蠭O多路復(fù)用,依然可以在高速的內(nèi)存處理中得到忽略。
(2)可維護(hù)性高
多線程模型雖然在某些方面表現(xiàn)優(yōu)異,但是它卻引入了程序執(zhí)行順序的不確定性,帶來(lái)了并發(fā)讀寫(xiě)的一系列問(wèn)題。單線程模式下,可以方便地進(jìn)行調(diào)試和測(cè)試。
(3)基于內(nèi)存,單線程狀態(tài)下效率依然高
多線程能夠充分利用CPU的資源,但對(duì)于Redis來(lái)說(shuō),由于基于內(nèi)存速度那是相當(dāng)?shù)母撸苓_(dá)到在一秒內(nèi)處理10萬(wàn)個(gè)用戶(hù)請(qǐng)求,如果一秒十萬(wàn)還不能滿(mǎn)足,那我們就可以使用Redis分片的技術(shù)來(lái)交給不同的Redis服務(wù)器。這樣的做飯避免了在同一個(gè) Redis 服務(wù)中引入大量的多線程操作。
而且基于內(nèi)存,除非是要進(jìn)行AOF備份,否則基本上不會(huì)涉及任何的 I/O 操作。這些數(shù)據(jù)的讀寫(xiě)由于只發(fā)生在內(nèi)存中,所以處理速度是非??斓模挥枚嗑€程模型處理全部的外部請(qǐng)求可能不是一個(gè)好的方案。
現(xiàn)在我們知道了基本上可以總結(jié)成兩句話,基于內(nèi)存而且使用多路復(fù)用技術(shù),單線程速度很快,又保證了多線程的特點(diǎn)。因?yàn)闆](méi)有必要使用多線程。
三、為什么引入多線程?
剛剛說(shuō)了一堆使用單線程的好處,現(xiàn)在話鋒一轉(zhuǎn),又要說(shuō)為什么要引入多線程,別不適應(yīng)。引入多線程說(shuō)明Redis在有些方面,單線程已經(jīng)不具有優(yōu)勢(shì)了。
因?yàn)樽x寫(xiě)網(wǎng)絡(luò)的read/write系統(tǒng)調(diào)用在Redis執(zhí)行期間占用了大部分CPU時(shí)間,如果把網(wǎng)絡(luò)讀寫(xiě)做成多線程的方式對(duì)性能會(huì)有很大提升。
Redis 的多線程部分只是用來(lái)處理網(wǎng)絡(luò)數(shù)據(jù)的讀寫(xiě)和協(xié)議解析,執(zhí)行命令仍然是單線程。之所以這么設(shè)計(jì)是不想 Redis 因?yàn)槎嗑€程而變得復(fù)雜,需要去控制 key、lua、事務(wù),LPUSH/LPOP 等等的并發(fā)問(wèn)題。
Redis 在最新的幾個(gè)版本中加入了一些可以被其他線程異步處理的刪除操作,也就是我們?cè)谏厦嫣岬降?UNLINK、FLUSHALL ASYNC 和 FLUSHDB ASYNC,我們?yōu)槭裁磿?huì)需要這些刪除操作,而它們?yōu)槭裁葱枰ㄟ^(guò)多線程的方式異步處理?
我們知道Redis可以使用del命令刪除一個(gè)元素,如果這個(gè)元素非常大,可能占據(jù)了幾十兆或者是幾百兆,那么在短時(shí)間內(nèi)是不能完成的,這樣一來(lái)就需要多線程的異步支持。
現(xiàn)在刪除工作可以在后臺(tái)進(jìn)行。
四、總結(jié)
Redis 選擇使用單線程模型處理客戶(hù)端的請(qǐng)求主要還是因?yàn)?CPU 不是 Redis 服務(wù)器的瓶頸,所以使用多線程模型帶來(lái)的性能提升并不能抵消它帶來(lái)的開(kāi)發(fā)成本和維護(hù)成本,系統(tǒng)的性能瓶頸也主要在網(wǎng)絡(luò) I/O 操作上;而 Redis 引入多線程操作也是出于性能上的考慮,對(duì)于一些大鍵值對(duì)的刪除操作,通過(guò)多線程非阻塞地釋放內(nèi)存空間也能減少對(duì) Redis 主線程阻塞的時(shí)間,提高執(zhí)行的效率。
一句話講完:之前用單線程是因?yàn)榛趦?nèi)存速度快,而且多路復(fù)用有多路復(fù)用的作用,也就是足夠了,現(xiàn)在引入是因?yàn)樵谀承┎僮饕獌?yōu)化,比如刪除操作,因此引入了多線程。
































