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