redis 最基本的一個內(nèi)部原理和特點,就是 redis 實際上是個單線程工作模型狱掂。
memcached 是早些年各大互聯(lián)網(wǎng)公司常用的緩存方案演痒,但是現(xiàn)在近幾年基本都是 redis,沒什么公司用 memcached 了趋惨。
redis 和 memcached 有啥區(qū)別鸟顺?
redis 支持復(fù)雜的數(shù)據(jù)結(jié)構(gòu)
redis 相比 memcached 來說,擁有更多的數(shù)據(jù)結(jié)構(gòu)器虾,能支持更豐富的數(shù)據(jù)操作讯嫂。如果需要緩存能夠支持更復(fù)雜的結(jié)構(gòu)和操作, redis 會是不錯的選擇兆沙。
redis 原生支持集群模式
在 redis3.x 版本中欧芽,便能支持 cluster 模式,而 memcached 沒有原生的集群模式葛圃,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù)千扔。
性能對比
由于 redis 只使用單核,而 memcached 可以使用多核装悲,所以平均每一個核上 redis 在存儲小數(shù)據(jù)時比 memcached 性能更高昏鹃。而在 100k 以上的數(shù)據(jù)中,memcached 性能要高于 redis诀诊,雖然 redis 最近也在存儲大數(shù)據(jù)的性能上進(jìn)行優(yōu)化洞渤,但是比起 memcached,還是稍有遜色属瓣。
redis 的線程模型
redis 內(nèi)部使用文件事件處理器 file event handler载迄,這個文件事件處理器是單線程的讯柔,所以 redis 才叫做單線程的模型。它采用 IO 多路復(fù)用機制同時監(jiān)聽多個 socket护昧,根據(jù) socket 上的事件來選擇對應(yīng)的事件處理器進(jìn)行處理魂迄。
文件事件處理器的結(jié)構(gòu)包含 4 個部分:
- 多個 socket
- IO 多路復(fù)用程序
- 文件事件分派器
- 事件處理器(連接應(yīng)答處理器、命令請求處理器惋耙、命令回復(fù)處理器)
多個 socket 可能會并發(fā)產(chǎn)生不同的操作捣炬,每個操作對應(yīng)不同的文件事件,但是 IO 多路復(fù)用程序會監(jiān)聽多個 socket绽榛,會將 socket 產(chǎn)生的事件放入隊列中排隊湿酸,事件分派器每次從隊列中取出一個事件,把該事件交給對應(yīng)的事件處理器進(jìn)行處理灭美。
來看客戶端與 redis 的一次通信過程(圖片若不清晰推溃,請右擊在新標(biāo)簽中打開圖片):
客戶端 socket01 向 redis 的 server socket 請求建立連接届腐,此時 server socket 會產(chǎn)生一個 AE_READABLE 事件铁坎,IO 多路復(fù)用程序監(jiān)聽到 server socket 產(chǎn)生的事件后,將該事件壓入隊列中犁苏。文件事件分派器從隊列中獲取該事件硬萍,交給連接應(yīng)答處理器。連接應(yīng)答處理器會創(chuàng)建一個能與客戶端通信的 socket01傀顾,并將該 socket01 的 AE_READABLE 事件與命令請求處理器關(guān)聯(lián)襟铭。
假設(shè)此時客戶端發(fā)送了一個 set key value 請求,此時 redis 中的 socket01 會產(chǎn)生 AE_READABLE 事件短曾,IO 多路復(fù)用程序?qū)⑹录喝腙犃校藭r事件分派器從隊列中獲取到該事件赐劣,由于前面 socket01 的 AE_READABLE 事件已經(jīng)與命令請求處理器關(guān)聯(lián)嫉拐,因此事件分派器將事件交給命令請求處理器來處理。命令請求處理器讀取 socket01 的 key value 并在自己內(nèi)存中完成 key value 的設(shè)置魁兼。操作完成后婉徘,它會將 socket01 的 AE_WRITABLE 事件與命令回復(fù)處理器關(guān)聯(lián)。
如果此時客戶端準(zhǔn)備好接收返回結(jié)果了咐汞,那么 redis 中的 socket01 會產(chǎn)生一個 AE_WRITABLE 事件盖呼,同樣壓入隊列中,事件分派器找到相關(guān)聯(lián)的命令回復(fù)處理器化撕,由命令回復(fù)處理器對 socket01 輸入本次操作的一個結(jié)果几晤,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件與命令回復(fù)處理器的關(guān)聯(lián)植阴。
這樣便完成了一次通信蟹瘾。
為啥 redis 單線程模型也能效率這么高圾浅?
- 純內(nèi)存操作
- 核心是基于非阻塞的 IO 多路復(fù)用機制
- 單線程反而避免了多線程的頻繁上下文切換問題
為什么Redis是單線程的
官方答案
因為Redis是基于內(nèi)存的操作,CPU不是Redis的瓶頸憾朴,Redis的瓶頸最有可能是機器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬狸捕。既然單線程容易實現(xiàn),而且CPU不會成為瓶頸众雷,那就順理成章地采用單線程的方案了灸拍。
性能指標(biāo)
關(guān)于redis的性能,官方網(wǎng)站也有砾省,普通筆記本輕松處理每秒幾十萬的請求鸡岗。
詳細(xì)原因
1)不需要各種鎖的性能消耗
Redis的數(shù)據(jù)結(jié)構(gòu)并不全是簡單的Key-Value,還有l(wèi)ist纯蛾,hash等復(fù)雜的結(jié)構(gòu)纤房,這些結(jié)構(gòu)有可能會進(jìn)行很細(xì)粒度的操作,比如在很長的列表后面添加一個元素翻诉,在hash當(dāng)中添加或者刪除一個對象炮姨。這些操作可能就需要加非常多的鎖,導(dǎo)致的結(jié)果是同步開銷大大增加碰煌。
總之舒岸,在單線程的情況下,就不用去考慮各種鎖的問題芦圾,不存在加鎖釋放鎖操作蛾派,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗。
2)單線程多進(jìn)程集群方案
單線程的威力實際上非常強大个少,每核心效率也非常高洪乍,多線程自然是可以比單線程有更高的性能上限,但是在今天的計算環(huán)境中夜焦,即使是單機多線程的上限也往往不能滿足需要了壳澳,需要進(jìn)一步摸索的是多服務(wù)器集群化的方案,這些方案中多線程的技術(shù)照樣是用不上的茫经。
所以單線程巷波、多進(jìn)程的集群不失為一個時髦的解決方案。
3)CPU消耗
采用單線程卸伞,避免了不必要的上下文切換和競爭條件抹镊,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU。
但是如果CPU成為Redis瓶頸荤傲,或者不想讓服務(wù)器其他CPU核閑置垮耳,那怎么辦?
可以考慮多起幾個Redis進(jìn)程弃酌,Redis是key-value數(shù)據(jù)庫氨菇,不是關(guān)系數(shù)據(jù)庫儡炼,數(shù)據(jù)之間沒有約束。只要客戶端分清哪些key放在哪個Redis進(jìn)程上就可以了查蓉。
Redis單線程的優(yōu)劣勢
單進(jìn)程單線程優(yōu)勢
代碼更清晰乌询,處理邏輯更簡單
不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作豌研,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗
不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗CPU單進(jìn)程單線程弊端
無法發(fā)揮多核CPU性能妹田,不過可以通過在單機開多個Redis實例來完善;
IO多路復(fù)用技術(shù)
redis 采用網(wǎng)絡(luò)IO多路復(fù)用技術(shù)來保證在多連接的時候鹃共, 系統(tǒng)的高吞吐量鬼佣。
多路-指的是多個socket連接,復(fù)用-指的是復(fù)用一個線程霜浴。多路復(fù)用主要有三種技術(shù):select晶衷,poll,epoll阴孟。epoll是最新的也是目前最好的多路復(fù)用技術(shù)晌纫。
這里“多路”指的是多個網(wǎng)絡(luò)連接,“復(fù)用”指的是復(fù)用同一個線程永丝。采用多路I/O復(fù)用技術(shù)可以讓單個線程高效的處理多個連接請求(盡量減少網(wǎng)絡(luò)IO的時間消耗)锹漱,且Redis在內(nèi)存中操作數(shù)據(jù)的速度非常快(內(nèi)存內(nèi)的操作不會成為這里的性能瓶頸)慕嚷,主要以上兩點造就了Redis具有很高的吞吐量哥牍。