摘要:Redis做為高性能的K-V數(shù)據(jù)庫,由于其高性能,豐富的數(shù)據(jù)結(jié)構(gòu)支持,易用等特性,而得到廣泛的應(yīng)用。但是由于redis單進(jìn)程單線程的模型限制,單Redis Server QPS最高只能達(dá)到10萬級(jí)別。本文試圖通過對(duì)Redis做多線程的優(yōu)化,來達(dá)到增強(qiáng)性能的目的。
背景
眾所周知redis是單進(jìn)程單線程模型(不完全是單進(jìn)程單線程,還有若干后端線程主要做刷臟數(shù)據(jù),關(guān)閉文件描述符等后臺(tái)清理工作)。redis中負(fù)責(zé)主要工作的是主線程,主線程的工作包括但不限:接收客戶端連接,處理連接讀寫事件,解析請求,處理命令,處理定時(shí)器事件,數(shù)據(jù)同步等相關(guān)工作。單進(jìn)程單線程只能跑滿一個(gè)CPU核,在小包場景下,單個(gè)redis server的QPS在8~10萬級(jí)別。如果QPS超過這個(gè)級(jí)別,單個(gè)redis server就無法滿足需求。而常用的解決辦法就是數(shù)據(jù)分片,采用多server的分布式架構(gòu)予以解決。然而數(shù)據(jù)分片,多redis server方式也存在若干問題:redis server過多,難以管理;分片之后一些在單redis server上使用的命令無法支持;分片無法解決熱點(diǎn)讀寫問題;分片后數(shù)據(jù)傾斜,數(shù)據(jù)重分布,數(shù)據(jù)擴(kuò)縮容等也比較復(fù)雜。由于單進(jìn)程單線程的局限,我們期望通過多線程的改造以期充分利用SMP多核架構(gòu)的優(yōu)勢,從而達(dá)到提高單redis server吞吐的目的。對(duì)redis做多線程化,最容易想到的方案是每個(gè)線程既做IO又做命令處理等工作,但由于redis處理的數(shù)據(jù)結(jié)構(gòu)相對(duì)比較復(fù)雜,多線程需要鎖來保證線程安全性,而鎖粒度處理不好性能反而可能會(huì)出現(xiàn)下降。
我們的思路是通過增加IO線程,將連接中數(shù)據(jù)的讀寫,命令的解析和數(shù)據(jù)包的回復(fù)放到單獨(dú)的IO線程來處理,而對(duì)命令的處理,定時(shí)器事件的執(zhí)行等仍讓單一的線程來處理,以此達(dá)到提高單redis server吞吐的目的。
單進(jìn)程單線程的優(yōu)點(diǎn)和不足
優(yōu)點(diǎn)
因?yàn)閱芜M(jìn)程單線程模型的限制,redis在實(shí)現(xiàn)上將耗時(shí)的操作分解成多步,多次來執(zhí)行(例如dict rehash, 過期key刪除等操作),盡量避免長時(shí)間執(zhí)行一個(gè)操作,從而避免長時(shí)間阻塞在一個(gè)操作上。單進(jìn)程單線程代碼編寫簡單,可以減少多進(jìn)程多線程導(dǎo)致的上下文切換和鎖的爭搶。
不足
只能使用一個(gè)CPU核,無法發(fā)揮多核優(yōu)勢。
對(duì)于重IO應(yīng)用來說,大量的cpu耗費(fèi)在網(wǎng)絡(luò)IO操作上。對(duì)于將redis做為緩存的應(yīng)用,往往都是重IO的應(yīng)用。這類應(yīng)用基本上都是QPS很高,使用的命令相對(duì)比較簡單(多為get,set,incr等操作),但是對(duì)RT響應(yīng)很敏感。這類應(yīng)用通常帶寬占用很高,甚至?xí)艿桨僬准?jí)別。當(dāng)前由于萬兆,25G網(wǎng)卡的普及,網(wǎng)絡(luò)往往已不再是瓶頸,而如何發(fā)揮多核優(yōu)勢,充分發(fā)揮網(wǎng)卡性能成為需要考慮的事情。
實(shí)現(xiàn)
線程劃分
主線程(MAIN THREAD)
IO線程(IO THREAD)
WORKER線程(WORKER THREAD)
線程模型

主線程:接受連接,創(chuàng)建client,將連接轉(zhuǎn)發(fā)給IO線程。
IO線程:處理連接的讀寫事件,解析命令,將解析的完整命令轉(zhuǎn)發(fā)給WORKER線程處理,發(fā)送response包,負(fù)責(zé)刪除連接等。
WORKER線程:負(fù)責(zé)命令的處理,生成客戶端回包,定時(shí)器事件的執(zhí)行等。
主線程,IO線程,WORKER線程都有單獨(dú)的事件驅(qū)動(dòng)。
線程之間通過無鎖隊(duì)列交換數(shù)據(jù),通過管道進(jìn)行消息通知。
收益
壓測結(jié)果

從壓測結(jié)果來看,小包場景下,讀寫性能差不多有三倍左右的性能提升。
主從同步速度提升
主從同步優(yōu)化
Master向Slave發(fā)送同步數(shù)據(jù)時(shí),數(shù)據(jù)在IO線程中發(fā)送,Slave從主讀取數(shù)據(jù)時(shí),全量數(shù)據(jù)在WORKER線程中讀取,增量數(shù)據(jù)在IO線程中讀取,因此可以相對(duì)比較有效的增加同步的速度。
后續(xù)工作
現(xiàn)在所做的第一部分工作是增加IO線程,優(yōu)化IO讀寫能力。進(jìn)一步的優(yōu)化可以考慮對(duì)WORKER線程進(jìn)行拆分:每個(gè)線程既負(fù)責(zé)IO讀取,也負(fù)責(zé)WORKER工作處理。
IO線程數(shù)設(shè)置
從測試結(jié)果來看,IO線程數(shù)最大不要超過6個(gè)。超過之后對(duì)簡單操作來說,WORKER線程往往已經(jīng)成為瓶頸。
進(jìn)程在啟動(dòng)時(shí)需要設(shè)置IO線程的個(gè)數(shù),在進(jìn)程運(yùn)行期間IO線程個(gè)數(shù)無法修改,按當(dāng)前的連接分配策略,修改IO線程的個(gè)數(shù)涉及到連接的重新分配,處理相對(duì)比較復(fù)雜。
展望
隨著萬兆網(wǎng)卡,25G網(wǎng)卡的普及,如何充分利用硬件的性能需要充分的考慮。多網(wǎng)絡(luò)IO線程,By pass內(nèi)核的用戶態(tài)協(xié)議棧等都是可利用的技術(shù)。
通過IO線程實(shí)現(xiàn)數(shù)據(jù)的遷移,可以無阻塞,IO線程對(duì)數(shù)據(jù)進(jìn)程Encode,或者命令轉(zhuǎn)發(fā),目標(biāo)節(jié)點(diǎn)實(shí)現(xiàn)數(shù)據(jù)Decode,或者命令執(zhí)行。