Redis的高性能和他的事件模型是密不可分的,最大程度上利用了單線程、非阻塞IO模型來快速的處理請求(單線程處理多鏈接)。這里存在一個問題,其實嚴格意義上來講,Redis 是單線程對外提供服務(wù),redis內(nèi)部并不單線程的,還存在一些關(guān)于數(shù)據(jù)持久化的線程。
在這里我們主要看的是Redis 對外提供服務(wù)的線程,Redis 很大程度上得益于單線程、非阻塞、多路復(fù)用的IO模型,就具體實現(xiàn)而言,Redis依賴的是一個專一且強大的異步事件庫(ea)。ae里面封裝了針對不同操作系統(tǒng)的polling機制,比如epoll、select等。
簡單的看一下這幾種polling模式
文件描述符(fd):
在Unix/Linux系統(tǒng)中,可以粗暴的認為一切都是文件。對于內(nèi)核而言,所有打開的文件都是通過文件描述符進行引用的,具體來說,內(nèi)核用一個文件描述符來表示一個特性進程正在訪問的文件,通常來說一個文件描述符的有效范圍是0到OPEN_MAX,就默認來說每個進程最多可以打開64個文件(0-63),對于 FreeBSD 8.0、Linux 3.2.0、Mac OS X 10.6.8 以及 Solaris 10 來說,文件描述符的變化范圍幾乎是無限的,它只受系統(tǒng)配置的存儲器總量、整型的字長以及系統(tǒng)管理員所配置的軟限制和硬限制的約束。然后最大文件描述符數(shù),Linux中進程最大打開文件描述符是1024,我們可以通過ulimit命令、修改limits.conf文件來進行最大數(shù)的修改。
這里需要注意一點容易被混淆的概念:/proc/sys/fs/file-max 并不是指最大文件描述符數(shù)的上限值。file-max指的是Linux內(nèi)核分配的最大文件句柄書、file-nr是一個(已經(jīng)分配的文件句柄數(shù)、已經(jīng)分配但沒有使用的文件句柄數(shù)、最大文件句柄數(shù))的三元組。
然后來看一下常見polling模式對比:
select:
1 每次select都要把全部IO句柄復(fù)制到內(nèi)核
2 內(nèi)核每次都要遍歷全部IO句柄,以判斷是否數(shù)據(jù)準備好
3 select模式最大IO句柄數(shù)是1024,太多了性能下降明顯
poll:
poll使用鏈表保存文件描述符,因此沒有了監(jiān)視文件數(shù)量的限制,但其他三個缺點依然存在。
拿select模型為例,假設(shè)我們的服務(wù)器需要支持100萬的并發(fā)連接,則在_FD_SETSIZE為1024的情況下,則我們至少需要開辟1k個進程才能實現(xiàn)100萬的并發(fā)連接。除了進程間上下文切換的時間消耗外,從內(nèi)核/用戶空間大量的無腦內(nèi)存拷貝、數(shù)組輪詢等,是系統(tǒng)難以承受的。因此,基于select模型的服務(wù)器程序,要達到10萬級別的并發(fā)訪問,是一個很難完成的任務(wù)。
epoll的特點
1 每次新建IO句柄(epoll_create)才復(fù)制并注冊(epoll_register)到內(nèi)核
2 內(nèi)核根據(jù)IO事件,把準備好的IO句柄放到就緒隊列
3 應(yīng)用只要輪詢(epoll_wait)就緒隊列,然后去讀取數(shù)據(jù)
只需要輪詢就緒隊列(數(shù)量少),不存在select的輪詢,也沒有內(nèi)核的輪詢,不需要多次復(fù)制所有的IO句柄。因此,可以同時支持的IO句柄數(shù)輕松過百萬。
很顯然,epoll模式是就當前來說最適合應(yīng)對高并發(fā)訪問的,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被更新時,kernel向應(yīng)用程序發(fā)送通知。
所以說有了epoll替我們做這些事兒,我們僅需要關(guān)注事件處理函數(shù)、回調(diào)函數(shù)就OK了。
下一篇看一下Redis中對于這種模式的實現(xiàn)。