引言:
上一節(jié)簡單介紹了redis的安裝與使用荠瘪,與redis一樣的緩存組件還有memcached,大體接入方式類似赛惩,這里就不重復(fù)介紹了哀墓,大家可以google具體memcached安裝以及使用方式。那么這一節(jié)將圍繞redis以及memcached兩大分布式最常用的緩存組件喷兼,對它們各自使用的網(wǎng)絡(luò)IO模型進(jìn)行分析篮绰。
簡介:
本篇博客主要針對redis的單線程模型,epoll事件驅(qū)動。以及memcached多線程模型,libevent事件驅(qū)動進(jìn)行對比分析褒搔,了解他們各自的設(shè)計思想與應(yīng)用層的利弊阶牍。
目錄:1.redis的單線程模型
? ? ? ? ? ?2.memcached的多線程模型
正文:?
引用一段CSDN博主對于redis架構(gòu)的描述:
Redis單線程架構(gòu)
1 單線程模型(linux只有輕量級進(jìn)程喷面,ps命令也會出線程,由于博主不搞底層開發(fā)走孽,就把模擬進(jìn)程稱為線程)
Redis客戶端對服務(wù)端的每次調(diào)用都經(jīng)歷了發(fā)送命令惧辈,執(zhí)行命令,返回結(jié)果三個過程磕瓷。其中執(zhí)行命令階段盒齿,由于Redis是單線程來處理命令的,所有每一條到達(dá)服務(wù)端的命令不會立刻執(zhí)行困食,所有的命令都會進(jìn)入一個隊列中边翁,然后逐個被執(zhí)行。并且多個客戶端發(fā)送的命令的執(zhí)行順序是不確定的硕盹。但是可以確定的是不會有兩條命令被同時執(zhí)行符匾,不會產(chǎn)生并發(fā)問題,這就是Redis的單線程基本模型瘩例。
(1)純內(nèi)存訪問啊胶。數(shù)據(jù)存放在內(nèi)存中,內(nèi)存的響應(yīng)時間大約是100納秒垛贤,這是Redis每秒萬級別訪問的重要基礎(chǔ)焰坪。
(2)非阻塞I/O,Redis采用epoll做為I/O多路復(fù)用技術(shù)的實現(xiàn)聘惦,再加上Redis自身的事件處理模型將epoll中的連接某饰,讀寫,關(guān)閉都轉(zhuǎn)換為了時間善绎,不在I/O上浪費過多的時間黔漂。
(3)單線程避免了線程切換和競態(tài)產(chǎn)生的消耗。
(4)Redis采用單線程模型涂邀,每條命令執(zhí)行如果占用大量時間瘟仿,會造成其他線程阻塞,對于Redis這種高性能服務(wù)是致命的比勉,所以Redis是面向高速執(zhí)行的數(shù)據(jù)庫。
ok,看完了以上描述驹止,我的總結(jié)是:
總體來說:
1)絕大部分請求是純粹的內(nèi)存操作(非澈屏快速)?
2)采用單線程,避免了不必要的上下文切換和競爭條件?
3)非阻塞IO 、epoll event loop臊恋。
Redis的單線程的行為主要是對內(nèi)存的讀寫衣洁,這些操作其實用不了多少時間,因此瓶頸在網(wǎng)絡(luò)I/O上面抖仅,我們一般提供較好的網(wǎng)絡(luò)環(huán)境就可以提升Redis的吞吐量坊夫,比如提高網(wǎng)絡(luò)帶寬砖第,除此之外還可以通過合并命令提交批處理請求(pipeline)來代替單條命令一次次請求從而減少網(wǎng)絡(luò)開銷,提高吞吐量环凿。
我們來進(jìn)一步研究redis的網(wǎng)絡(luò)模型梧兼,redis是封裝了下圖幾種多路復(fù)用器實現(xiàn)的EventLoop事件輪訓(xùn),redis沒有采用memcached采用的Libevent智听,Libevent為了迎合通用性造成代碼龐大及犧牲了在特定平臺的不少性能羽杰。Redis一直堅持設(shè)計小巧并去依賴庫的思路,提供Socket句柄事件的多路復(fù)用器到推,這部分分別對于不同平臺提供了不同的實現(xiàn)考赛,比如epoll和select可以用于linux平臺、kqueue可以用于蘋果平臺莉测、evpoll可以用于Solaris平臺颜骤,這里并沒有看到iocp,也就是Redis對于Windows支持并不是很好捣卤。
什么是多路I/O復(fù)用忍抽?
(1) 網(wǎng)絡(luò)IO都是通過Socket實現(xiàn),Server在某一個端口持續(xù)監(jiān)聽腌零,客戶端通過Socket(IP+Port)與服務(wù)器建立連接(ServerSocket.accept)梯找,成功建立連接之后,就可以使用Socket中封裝的InputStream和OutputStream進(jìn)行IO交互了益涧。針對每個客戶端锈锤,Server都會創(chuàng)建一個新線程專門用于處理?
(2) 默認(rèn)情況下,網(wǎng)絡(luò)IO是阻塞模式闲询,即服務(wù)器線程在數(shù)據(jù)到來之前處于【阻塞】狀態(tài)久免,等到數(shù)據(jù)到達(dá),會自動喚醒服務(wù)器線程扭弧,著手進(jìn)行處理阎姥。阻塞模式下,一個線程只能處理一個流的IO事件?
(3) 為了提升服務(wù)器線程處理效率鸽捻,有以下三種思路
(1)非阻塞【忙輪詢】:采用死循環(huán)方式輪詢每一個流呼巴,如果有IO事件就處理,這樣可以使得一個線程可以處理多個流御蒲,但是效率不高衣赶,容易導(dǎo)致CPU空轉(zhuǎn)
(2)Select代理(無差別輪詢):可以觀察多個流的IO事件,如果所有流都沒有IO事件厚满,則將線程進(jìn)入阻塞狀態(tài)府瞄,如果有一個或多個發(fā)生了IO事件,則喚醒線程去處理碘箍。但是還是得遍歷所有的流遵馆,才能找出哪些流需要處理鲸郊。
(3)Epoll代理:Select代理有一個缺點,線程在被喚醒后輪詢所有的Stream货邓,還是存在無效操作秆撮。 Epoll會哪個流發(fā)生了怎樣的I/O事件通知處理線程,因此對這些流的操作都是有意義的逻恐。
Linux下epoll屬于IO多路復(fù)用像吻,但他實際應(yīng)用必須搭配no-blocking io ,執(zhí)行epoll和具體業(yè)務(wù)都是在同個主進(jìn)程中執(zhí)行复隆。雖然純內(nèi)存的業(yè)務(wù)操作很快拨匆,但在執(zhí)行業(yè)務(wù)時,有新的請求到來那么kernel中發(fā)現(xiàn)readylist正在被使用時挽拂,會把就緒事件放在ovflist中當(dāng)處理完readylist后惭每,會檢查ovflist是否有事件。
首先亏栈,Redis服務(wù)器中有兩類事件台腥,文件事件和時間事件。
文件事件(file event):Redis客戶端通過socket與Redis服務(wù)器連接绒北,而文件事件就是服務(wù)器對套接字操作的抽象黎侈。例如,客戶端發(fā)了一個GET命令請求闷游,對于Redis服務(wù)器來說就是一個文件事件峻汉。
時間事件(time event):服務(wù)器定時或周期性執(zhí)行的事件。例如脐往,定期執(zhí)行RDB持久化休吠。
在這里我們主要關(guān)注Redis處理文件事件的模型:Reactor模型
Handles?:表示操作系統(tǒng)管理的資源,我們可以理解為fd业簿。
Synchronous Event Demultiplexer?:同步事件分離器瘤礁,阻塞等待Handles中的事件發(fā)生。
Initiation Dispatcher?:初始分派器梅尤,作用為添加Event handler(事件處理器)柜思、刪除Event handler以及分派事件給Event handler。也就是說巷燥,Synchronous Event Demultiplexer負(fù)責(zé)等待新事件發(fā)生酝蜒,事件發(fā)生時通知Initiation Dispatcher,然后Initiation Dispatcher調(diào)用event handler處理事件矾湃。
Event Handler?:事件處理器的接口
Concrete Event Handler?:事件處理器的實際實現(xiàn),而且綁定了一個Handle堕澄。因為在實際情況中邀跃,我們往往不止一種事件處理器霉咨,因此這里將事件處理器接口和實現(xiàn)分開,與C++拍屑、Java這些高級語言中的多態(tài)類似途戒。
2.memcached網(wǎng)絡(luò)IO模型
Memcached是多線程,非阻塞IO復(fù)用的網(wǎng)絡(luò)模型僵驰,分為監(jiān)聽主線程和worker子線程喷斋,監(jiān)聽線程監(jiān)聽網(wǎng)絡(luò)連接,接受請求后蒜茴,將連接描述字pipe 傳遞給worker線程星爪,進(jìn)行讀寫IO, 網(wǎng)絡(luò)層使用libevent封裝的事件庫,多線程模型可以發(fā)揮多核作用粉私,但是引入了cache coherency和鎖的問題顽腾,比如,Memcached最常用的stats 命令诺核,實際Memcached所有操作都要對這個全局變量加鎖抄肖,進(jìn)行計數(shù)等工作,帶來了性能損耗窖杀。
Libevent應(yīng)該算是包含了上述一些功能漓摩,總體來說也是走事件驅(qū)動+no blocking io。會有一個fd和event的mapping關(guān)系入客,至于linux內(nèi)核對于喚醒的細(xì)節(jié)就沒有多研究了管毙,那總體對比下來,io層面的話痊项,redis有單線程的優(yōu)勢锅风、批量pipeline的優(yōu)勢,memcached有多核優(yōu)勢鞍泉,但是多了一些同步的損耗皱埠,但是在內(nèi)存管理方面以及集群方面,redis是動態(tài)分配咖驮,memcached是分配比較固定的chunk边器,那對于某些字節(jié)比chunk小的情況,就有浪費的空間托修,集群方面memcached服務(wù)端是沒有分布式的忘巧,redis在3.0提供了cluster,這個后續(xù)討論睦刃,具體性能對比還需要區(qū)分?jǐn)?shù)據(jù)量 數(shù)據(jù)格式 集群方案等砚嘴,下篇文章主要分析兩者在內(nèi)存管理上的區(qū)別。