Redis系列(六)--爲何這麼快?

Redis做爲一個基於key-value的NoSQL數據庫,最顯著的特色存取速度很是快,官方說能夠達到10W OPS,可是Redis爲什麼這麼快?算法

一、開發語言

Redis使用C語言進行編寫的,而Unix系統也是C語言實現,因此C語言是很是貼近操做系統的語言數據庫

二、基於內存讀寫

基於內存讀寫是Redis速度快的主要緣由,不進行數據同步的狀況下,不從磁盤讀取數據,沒有IO。內存響應時間大約100ns數組

三、單線程

  1).單線程避免了線程上下文切換以及同步加鎖、解鎖帶來的消耗。服務器

  2).單線程簡化算法的實現網絡

  3).單線程也帶來一個問題,阻塞,對於一個長命令來講,會阻塞不少命令的執行響應數據結構

這裏的單線程,不包含fork()產生的子進程。除了Redis以外,Node.js、Nginx都是單線程,都屬於高性能的組件框架併發

四、多路I/O複用模型

  因爲Redis是單線程的,全部的操做都是串行執行,可是因爲讀寫操做等待用戶輸入或輸出都是阻塞的,因此 I/O 操做在通常狀況下每每不能直框架

接返回,這會致使某一文件的 I/O 阻塞致使整個進程沒法對其它客戶提供服務,而 I/O 多路複用就是爲了解決這個問題而出現的。高併發

  Redis的I/O模型基於epoll實現,也提供select和kqueue的實現,默認epoll性能

epoll相對於其餘多路複用技術,具備的優勢:

  1. epoll沒有最大併發鏈接的限制,上限是最大能夠打開文件的數目,這個數字通常遠大於 2048

  2. 效率提高,epoll最大的優勢就在於它只管你「活躍」的鏈接,而跟鏈接總數無關,所以在實際的網絡環境中, epoll的效率就會遠遠高於

select和poll

  3. 內存拷貝,epoll在這點上使用了「共享內存 」,這個內存拷貝也省略了。

 

epoll與select/poll的區別:

I/O多路複用:經過一種機制,能夠監視多個描述符(File Descriptor,簡稱fd),一旦某個描述符就緒,可以通知程序進行相應的操做。

一、select:

  本質是採用32個整數的32位,即32*32 = 1024來標識,fd值爲1-1024。當fd的值超過1024限制時,就必須修改FD_SETSIZE的大小。這個時候就

能夠標識32*max值範圍的fd。

二、poll:

  poll與select不一樣,經過一個pollfd數組向內核傳遞須要關注的事件,故沒有描述符個數的限制,pollfd中的events字段和revents分別用於

標示關注的事件和發生的事件,故pollfd數組只須要被初始化一次。

三、epoll:

  是poll的一種優化,返回後不須要對全部的fd進行遍歷,在內核中維持了fd的列表。select和poll是將這個內核列表維持在用戶態,而後傳遞

到內核中。與poll/select不一樣,epoll再也不是一個單獨的系統調用,而是由epoll_create/epoll_ctl/epoll_wait三個系統調用組成,後面將會看到

這樣作的好處。epoll在2.6之後的內核才支持。

 

select/poll的幾大缺點:

  一、每次調用select/poll,都須要把fd集合從用戶態拷貝到內核態,這個開銷在fd不少時會很大

  二、同時每次調用select/poll都須要在內核遍歷傳遞進來的全部fd,這個開銷在fd不少時也很大

  三、針對select支持的文件描述符數量過小了,默認是1024

  4.select返回的是含有整個句柄的數組,應用程序須要遍歷整個數組才能發現哪些句柄發生了事件;

  5.select的觸發方式是水平觸發,應用程序若是沒有完成對一個已經就緒的文件描述符進行IO操做,那麼以後每次select調用仍是會將這些文

件描述符通知進程。相比select模型,poll使用鏈表保存文件描述符,所以沒有了監視文件數量的限制,但其餘三個缺點依然存在。

 

epoll IO多路複用模型實現機制:

  因爲epoll的實現機制與select/poll機制徹底不一樣,上面所說的 select的缺點在epoll上不復存在。

  epoll沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目,這個數字通常遠大於2048,舉個例子,在1GB內存的機器上大約是10萬左右

 

  設想一下以下場景:有100萬個客戶端同時與一個服務器進程保持着TCP鏈接。而每一時刻,一般只有幾百上千個TCP鏈接是活躍的(事實上大部

分場景都是這種狀況)。

如何實現這樣的高併發?

  在select/poll時代,服務器進程每次都把這100萬個鏈接告訴操做系統(從用戶態複製句柄數據結構到內核態),讓操做系統內核去查詢這些套

接字上是否有事件發生,輪詢完後,再將句柄數據複製到用戶態,讓服務器應用程序輪詢處理已發生的網絡事件,這一過程資源消耗較大,所以,

select/poll通常只能處理幾千的併發鏈接。

  若是沒有I/O事件產生,咱們的程序就會阻塞在select處。可是依然有個問題,咱們從select那裏僅僅知道了,有I/O事件發生了,但卻並不知

道是那幾個流(可能有一個,多個,甚至所有),咱們只能無差異輪詢全部流,找出能讀出數據,或者寫入數據的流,對他們進行操做。可是使用

select,咱們有O(n)的無差異輪詢複雜度,同時處理的流越多,每一次無差異輪詢時間就越長

 

epoll的設計和實現與select徹底不一樣。epoll經過在Linux內核中申請一個簡易的文件系統(文件系統通常用什麼數據結構實現?B+樹)。把原先的

select/poll調用分紅了3個部分:

  1)調用epoll_create()創建一個epoll對象(在epoll文件系統中爲這個句柄對象分配資源)

  2)調用epoll_ctl向epoll對象中添加這100萬個鏈接的套接字

  3)調用epoll_wait收集發生的事件的鏈接

  如此一來,要實現上面說是的場景,只須要在進程啓動時創建一個epoll對象,而後在須要的時候向這個epoll對象中添加或者刪除鏈接。同時,

epoll_wait的效率也很是高,由於調用epoll_wait時,並無一股腦的向操做系統複製這100萬個鏈接的句柄數據,內核也不須要去遍歷所有的鏈接。

 

epoll內容原文地址:http://www.javashuo.com/article/p-ocfrejfu-ng.html

相關文章
相關標籤/搜索