Redis 6.0多線程探秘之一
本文轉(zhuǎn)載自微信公眾號「后端技術(shù)指南針」,作者大白 。轉(zhuǎn)載本文請聯(lián)系后端技術(shù)指南針公眾號。
1.老牌八股面試題
想必我們經(jīng)常聽到一個爛大街面試題:
Redis為什么選擇單線程?
這種問法其實并不嚴(yán)謹(jǐn),為啥這么說呢:
- Redis的版本很多3.x、4.x、6.x,版本不同架構(gòu)也是不同的,不限定版本問這種問題,是不是有點耍流氓。
 - 限定版本之后 比如4.x,嚴(yán)格意義來說Redis也不是單線程,而是負責(zé)處理客戶端請求的線程是單線程。
 - 最新版本的6.0版本,告別了大家印象中的單線程,用一種全新的多線程來解決問題。
 
你要是這么一回答,面試官估計都會想:
啊呀,碰到行家了,反正這個問題我也不太清楚,好好聽下他咋解決吧!
Redis的版本迭代和里程碑
Redis從發(fā)布至今,已經(jīng)有十余年的時光了,一直遵循著自己的命名規(guī)則:
- 版本號第二位如果是奇數(shù),則為非穩(wěn)定版本 如2.7、2.9、3.1
 - 版本號第二位如果是偶數(shù),則為穩(wěn)定版本 如2.6、2.8、3.0、3.2
 - 當(dāng)前奇數(shù)版本就是下一個穩(wěn)定版本的開發(fā)版本,如2.9版本是3.0版本的開發(fā)版本
 
我們在生產(chǎn)環(huán)境一般都會選擇穩(wěn)定版本來部署,在每個大版本之間還會有若干個小版本,目前最新的版本是6.2.2。
我們可以通過redis.io官網(wǎng)來下載自己感興趣的版本進行源碼閱讀:
歷史發(fā)布版本的源碼:https://download.redis.io/releases/
其中有幾個里程碑式的版本,需要我們了解下:
5.0版本是直接升級到6.0版本,對于這個激進的升級,antirez表現(xiàn)得很有信心和興奮,所以第一時間發(fā)文來闡述6.0的一些重大功能"Redis 6.0.0 GA is out!":
http://antirez.com/news/132
注:GA是Generally Available的縮寫,意思是開發(fā)團隊認為該版本是穩(wěn)定版。
扯了這么多,就是希望大家清楚一點,Redis是與時俱進的,千萬不要以為Redis就是一直是那個單線程。
2.不一樣的Redis之父
我們常說字如其人,對于我們程序員來說,碼如其人,也是十分貼切。
從多個歷史版本中我們隱約可以感覺到Redis之父Antirez是個很特別的人。
大白你這說的不是廢話嘛,畢竟是頂流扛把子程序員,怎么會輕易隨波逐流。
還是舉個實際的例子感受一樣,什么叫總能搞點不一樣的。
集群方案
Redis單機版出來之后,官方集群版遲遲沒有發(fā)布,這個時候業(yè)界就出了一些集群方案,比較有代表性的是codis和Tweproxy,這兩種方案都是中心化的方案以及中間層的思想。
由于市場的需求旺盛,這兩種方案很快被很多公司應(yīng)用到生產(chǎn)環(huán)境,然而官方集群方案卻遲遲沒有發(fā)布,這一等就是4年,直到2015年4月1號才發(fā)布。
同樣地Antirez還是激動地發(fā)了一篇文章"Redis cluster, no longer vaporware."
http://antirez.com/news/79
備注:標(biāo)題可以譯為Redis集群不再是幻想。
在官方集群方案中采用了P2P模式去中心化的思想、借助slot來實現(xiàn)一致性哈希、以及gossip協(xié)議來實現(xiàn)集群通信,整體架構(gòu)更加簡潔。
3.Redis 6.0多線程的神秘面紗
Redis作為內(nèi)存型NoSQL可以說是高性能的代名詞,生產(chǎn)環(huán)境中數(shù)萬QPS都是家常便飯。
試想一下,Redis如何進一步來提高性能呢?這恐怕也是Redis之父苦苦思索的問題。
擒賊先擒王,要提高性能,就要看看是什么卡脖子了。
Redis的瓶頸是什么
通常來說多線程對于提高CPU利用率有重要作用,但是Redis對于提高CPU利用率并不感冒,在Redis看來如果要提高CPU利用率,那在一臺機器部署多個實例就好了。
其實想想Redis之所以那么青睞單線程,肯定是嘗到了單線程的甜頭:
- 業(yè)務(wù)模型簡單,并發(fā)讀寫沒問題
 - 單線程完全無鎖化 不死鎖無線程切換損耗,性能賊好
 - 處理底層復(fù)雜的數(shù)據(jù)結(jié)構(gòu)時有線程安全做保證,十分放心
 - ......
 
其實在Redis 4.0就引入了多個線程來實現(xiàn)數(shù)據(jù)的異步刪除等功能,但是其處理讀寫請求的仍然只有一個線程,所以仍然算是狹義上的單線程。
拋開CPU之后,影響Redis性能的地方主要就剩下:內(nèi)存和網(wǎng)絡(luò)IO。
內(nèi)存更多屬于硬件范疇的東西,比如我們用容量更大、吞吐率更高的內(nèi)存介質(zhì)來進行優(yōu)化,因此對于Redis來說可以優(yōu)化的空間有限。
最后Redis的瓶頸可以初步定為:網(wǎng)絡(luò)IO。
Redis的基本架構(gòu)
在優(yōu)化網(wǎng)絡(luò)IO之前,我們有必要回顧下Redis單線程整體架構(gòu):
Redis采用Reactor模式的網(wǎng)絡(luò)模型,對于一個客戶端請求,主線程負責(zé)一個完整的處理過程:
從socket中讀取數(shù)據(jù)和往socket寫數(shù)據(jù)都是比較耗時的網(wǎng)絡(luò)IO操作,解析請求和內(nèi)存交互耗時可能遠小于IO操作。
對于這種問題,我們常見的解決方法是標(biāo)準(zhǔn)的多線程化:
該方案中工作線程的功能是一樣的,MemCached就是采用這種方案,具體的流程:
Memcached采用 master-woker 模式進行工作,主線程采用 Libevent 監(jiān)聽和處理事件,主線程將連接請求封裝為任務(wù)放到隊列中,根據(jù)算法選擇空閑的工作線程,相應(yīng)的工作線程處理完成后通過soeket與客戶端進行數(shù)據(jù)交互。
但是Redis 6.0的多線程并沒有這么做。
Redis自己的多線程
單線程給Redis帶來的好處,或許更大。
另外一點如果做成標(biāo)準(zhǔn)化的多線程,對于Redis來說可能更不好處理,因為多線程帶來的線程安全問題和底層復(fù)雜的數(shù)據(jù)結(jié)構(gòu)操作都十分棘手。
Redis 6.0將處理過程中最耗時的Socket的讀取、請求解析、寫入單獨外包出去,剩下的命令執(zhí)行仍然由單線程來完成和內(nèi)存的數(shù)據(jù)交互。
這樣一來,網(wǎng)絡(luò)IO操作就變成多線程化了,其他核心部分仍然是線程安全的,確實是個不錯的折中辦法。
畫外音:Redis 6.0 將網(wǎng)絡(luò)數(shù)據(jù)讀寫、請求協(xié)議解析通過多個IO線程的來處理 ,對于真正的命令執(zhí)行來說,仍然使用主線程操作,真是個很特別的多線程啊!
4.小結(jié)
本文最多算個入門篇,關(guān)于Redis多線程的更大細節(jié),我們下期再搞。























 
 
 









 
 
 
 