Redis的高性能和他的事件模型是密不可分的,最大程度上利用了單線程、非阻塞IO模型來(lái)快速的處理請(qǐng)求(單線程處理多鏈接)遥缕。這里存在一個(gè)問(wèn)題,其實(shí)嚴(yán)格意義上來(lái)講宵呛,Redis 是單線程對(duì)外提供服務(wù)单匣,redis內(nèi)部并不單線程的,還存在一些關(guān)于數(shù)據(jù)持久化的線程宝穗。
在這里我們主要看的是Redis 對(duì)外提供服務(wù)的線程户秤,Redis 很大程度上得益于單線程、非阻塞逮矛、多路復(fù)用的IO模型鸡号,就具體實(shí)現(xiàn)而言,Redis依賴的是一個(gè)專一且強(qiáng)大的異步事件庫(kù)(ea)须鼎。ae里面封裝了針對(duì)不同操作系統(tǒng)的polling機(jī)制鲸伴,比如epoll、select等晋控。
簡(jiǎn)單的看一下這幾種polling模式
文件描述符(fd):
在Unix/Linux系統(tǒng)中汞窗,可以粗暴的認(rèn)為一切都是文件。對(duì)于內(nèi)核而言赡译,所有打開(kāi)的文件都是通過(guò)文件描述符進(jìn)行引用的仲吏,具體來(lái)說(shuō),內(nèi)核用一個(gè)文件描述符來(lái)表示一個(gè)特性進(jìn)程正在訪問(wèn)的文件蝌焚,通常來(lái)說(shuō)一個(gè)文件描述符的有效范圍是0到OPEN_MAX裹唆,就默認(rèn)來(lái)說(shuō)每個(gè)進(jìn)程最多可以打開(kāi)64個(gè)文件(0-63),對(duì)于 FreeBSD 8.0综看、Linux 3.2.0品腹、Mac OS X 10.6.8 以及 Solaris 10 來(lái)說(shuō)岖食,文件描述符的變化范圍幾乎是無(wú)限的红碑,它只受系統(tǒng)配置的存儲(chǔ)器總量、整型的字長(zhǎng)以及系統(tǒng)管理員所配置的軟限制和硬限制的約束泡垃。然后最大文件描述符數(shù)析珊,Linux中進(jìn)程最大打開(kāi)文件描述符是1024,我們可以通過(guò)ulimit命令蔑穴、修改limits.conf文件來(lái)進(jìn)行最大數(shù)的修改忠寻。
這里需要注意一點(diǎn)容易被混淆的概念:/proc/sys/fs/file-max 并不是指最大文件描述符數(shù)的上限值。file-max指的是Linux內(nèi)核分配的最大文件句柄書(shū)存和、file-nr是一個(gè)(已經(jīng)分配的文件句柄數(shù)奕剃、已經(jīng)分配但沒(méi)有使用的文件句柄數(shù)衷旅、最大文件句柄數(shù))的三元組。
然后來(lái)看一下常見(jiàn)polling模式對(duì)比:
select:
1 每次select都要把全部IO句柄復(fù)制到內(nèi)核
2 內(nèi)核每次都要遍歷全部IO句柄纵朋,以判斷是否數(shù)據(jù)準(zhǔn)備好
3 select模式最大IO句柄數(shù)是1024柿顶,太多了性能下降明顯
poll:
poll使用鏈表保存文件描述符,因此沒(méi)有了監(jiān)視文件數(shù)量的限制操软,但其他三個(gè)缺點(diǎn)依然存在嘁锯。
拿select模型為例,假設(shè)我們的服務(wù)器需要支持100萬(wàn)的并發(fā)連接聂薪,則在_FD_SETSIZE為1024的情況下家乘,則我們至少需要開(kāi)辟1k個(gè)進(jìn)程才能實(shí)現(xiàn)100萬(wàn)的并發(fā)連接。除了進(jìn)程間上下文切換的時(shí)間消耗外藏澳,從內(nèi)核/用戶空間大量的無(wú)腦內(nèi)存拷貝仁锯、數(shù)組輪詢等,是系統(tǒng)難以承受的翔悠。因此扑馁,基于select模型的服務(wù)器程序,要達(dá)到10萬(wàn)級(jí)別的并發(fā)訪問(wèn)凉驻,是一個(gè)很難完成的任務(wù)腻要。
epoll的特點(diǎn)
1 每次新建IO句柄(epoll_create)才復(fù)制并注冊(cè)(epoll_register)到內(nèi)核
2 內(nèi)核根據(jù)IO事件,把準(zhǔn)備好的IO句柄放到就緒隊(duì)列
3 應(yīng)用只要輪詢(epoll_wait)就緒隊(duì)列涝登,然后去讀取數(shù)據(jù)
只需要輪詢就緒隊(duì)列(數(shù)量少)雄家,不存在select的輪詢,也沒(méi)有內(nèi)核的輪詢胀滚,不需要多次復(fù)制所有的IO句柄趟济。因此,可以同時(shí)支持的IO句柄數(shù)輕松過(guò)百萬(wàn)咽笼。
很顯然顷编,epoll模式是就當(dāng)前來(lái)說(shuō)最適合應(yīng)對(duì)高并發(fā)訪問(wèn)的,epoll是這樣工作的:
1剑刑、調(diào)用epoll_create 通知kernel我們要使用epoll
2媳纬、調(diào)用epoll_ctl把fd和關(guān)注的event傳給kernel
3、調(diào)用epoll_wait等待該event的發(fā)生
4施掏、fd被更新時(shí)钮惠,kernel向應(yīng)用程序發(fā)送通知。
所以說(shuō)有了epoll替我們做這些事兒七芭,我們僅需要關(guān)注事件處理函數(shù)素挽、回調(diào)函數(shù)就OK了。
下一篇看一下Redis中對(duì)于這種模式的實(shí)現(xiàn)狸驳。