阿里雲Redis多線程性能提升思路解析

背景

衆所周知redis是單進程單線程模型(不完全是單進程單線程,還有若干後端線程主要做刷髒數據,關閉文件描述符等後臺清理工作)。redis中負責主要工作的是主線程,主線程的工作包括但不限:接收客戶端連接,處理連接讀寫事件,解析請求,處理命令,處理定時器事件,數據同步等相關工作。單進程單線程只能跑滿一個CPU核,在小包場景下,單個redis server的QPS在8~10萬級別。如果QPS超過這個級別,單個redis server就無法滿足需求。而常用的解決辦法就是數據分片,採用多server的分佈式架構予以解決。然而數據分片,多redis server方式也存在若干問題:redis server過多,難以管理;分片之後一些在單redis server上使用的命令無法支持;分片無法解決熱點讀寫問題;分片後數據傾斜,數據重分佈,數據擴縮容等也比較複雜。由於單進程單線程的侷限,我們期望通過多線程的改造以期充分利用SMP多核架構的優勢,從而達到提高單redis server吞吐的目的。對redis做多線程化,最容易想到的方案是每個線程既做IO又做命令處理等工作,但由於redis處理的數據結構相對比較複雜,多線程需要鎖來保證線程安全性,而鎖粒度處理不好性能反而可能會出現下降。

我們的思路是通過增加IO線程,將連接中數據的讀寫,命令的解析和數據包的回覆放到單獨的IO線程來處理,而對命令的處理,定時器事件的執行等仍讓單一的線程來處理,以此達到提高單redis server吞吐的目的。

單進程單線程的優點和不足

優點

因爲單進程單線程模型的限制,redis在實現上將耗時的操作分解成多步,多次來執行(例如dict rehash, 過期key刪除等操作),儘量避免長時間執行一個操作,從而避免長時間阻塞在一個操作上。單進程單線程代碼編寫簡單,可以減少多進程多線程導致的上下文切換和鎖的爭搶。
不足

只能使用一個CPU核,無法發揮多核優勢。
對於重IO應用來說,大量的cpu耗費在網絡IO操作上。對於將redis做爲緩存的應用,往往都是重IO的應用。這類應用基本上都是QPS很高,使用的命令相對比較簡單(多爲get,set,incr等操作),但是對RT響應很敏感。這類應用通常帶寬佔用很高,甚至會跑到百兆級別。當前由於萬兆,25G網卡的普及,網絡往往已不再是瓶頸,而如何發揮多核優勢,充分發揮網卡性能成爲需要考慮的事情。
實現

線程劃分

主線程(MAIN THREAD)
IO線程(IO THREAD)
WORKER線程(WORKER THREAD)

線程模型

mio21.jpg

主線程:接受連接,創建client,將連接轉發給IO線程。
IO線程:處理連接的讀寫事件,解析命令,將解析的完整命令轉發給WORKER線程處理,發送response包,負責刪除連接等。
WORKER線程:負責命令的處理,生成客戶端回包,定時器事件的執行等。
主線程,IO線程,WORKER線程都有單獨的事件驅動。
線程之間通過無鎖隊列交換數據,通過管道進行消息通知。
收益

壓測結果

io27.jpg

從壓測結果來看,小包場景下,讀寫性能差不多有三倍左右的性能提升。
主從同步速度提升

主從同步優化

Master向Slave發送同步數據時,數據在IO線程中發送,Slave從主讀取數據時,全量數據在WORKER線程中讀取,增量數據在IO線程中讀取,因此可以相對比較有效的增加同步的速度。
後續工作

現在所做的第一部分工作是增加IO線程,優化IO讀寫能力。進一步的優化可以考慮對WORKER線程進行拆分:每個線程既負責IO讀取,也負責WORKER工作處理。
IO線程數設置

從測試結果來看,IO線程數最大不要超過6個。超過之後對簡單操作來說,WORKER線程往往已經成爲瓶頸。
進程在啓動時需要設置IO線程的個數,在進程運行期間IO線程個數無法修改,按當前的連接分配策略,修改IO線程的個數涉及到連接的重新分配,處理相對比較複雜。
展望

隨着萬兆網卡,25G網卡的普及,如何充分利用硬件的性能需要充分的考慮。多網絡IO線程,By pass內核的用戶態協議棧等都是可利用的技術。
通過IO線程實現數據的遷移,可以無阻塞,IO線程對數據進程Encode,或者命令轉發,目標節點實現數據Decode,或者命令執行。
原文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章