單線程的REDIS為什么這么快?


REDIS是單線程處理所有請求,和一般經典實際上推薦的方式相反,那么單線程串行處理,為什么依然能夠做到很快呢?知乎上的一個答案如下, 其中線程切換和鎖不是性能主要影響因素的觀點和一般的答案都不同
作者:楊海坡
鏈接:https://www.zhihu.com/question/19764056/answer/20241839
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

純內存數據庫,如果只是簡單的 key-value,內存不是瓶頸。一般情況下,hash 查找可以達到每秒數百萬次的數量級。

瓶頸在於網絡 IO 上。

根據你測的的 10000/s 來看,客戶端和 redis 應該是部署在兩台不同的機器,並且是使用同步的方式請求 redis. 每次請求需要通過網絡把請求發送到 redis 所在的機器,然后等待 redis 返回數據。時間大部分消耗在網絡傳輸中。

如果把 redis 和客戶端放在同一台機器,網絡延遲會更小,一般情況下可以打到 60000 次每秒甚至更高,取決於機器性能。

鎖不是影響性能的主要因素。線程鎖 (mutex_lock) 只有在遇到沖突的情況下性能會下降,而正常情況下,遇到沖突的概率很低。如果只是簡單的加鎖、釋放鎖速度是非常快的,每秒鍾上千萬次沒問題。memcache 內部用到了大量的鎖,並沒有見到性能降低。

線程也不是影響吞吐量的重要因素。如第一點來說,一般情況下,程序處理內存數據的速度遠高於網卡接收的速度。使用線程好處是可以同時處理多條連接,在極端情況下,可能會提高響應速度。

使用 epoll 或 libevent 等因為異步非阻塞 IO 編程只能這么做。與之對應的是同步阻塞 IO 編程,使用多進程或多線程實現多條連接的處理,比如 apache。一般情況下,異步非阻塞 IO 模型性能是遠高於同步阻塞 IO 模型的,可以參考 nginx 與 apache 性能的對比。

libevent 並不比 redis 自己實現的 ae_event 慢,代碼多是應為 ae_event 只實現了 redis 需要的功能,而 libevent 則具有更多的功能,比如更快的定時器、buffer event 模型,甚至自帶了 DNS、HTTP 協議的處理。並且 libevent 更通用,而 redis 只專注於 linux 平台。

最后回答題主問題,快在哪?
1、純內存操作
2、異步非阻塞 IO
 
本博客的個人觀點:
1. REDIS很注重讓線程的每次操作都盡量簡短,會將功能細分成多個邏輯塊,每個邏輯塊都分別讓線程在自己的獨立的時間片完成,而不是一個時間片做整個功能。
    比如,主從服務器的復制功能。從服務器收到SLAVE OF的命令后,會記錄信息,然后交由serverCron來進行socket建鏈,然后再次交由serverCron進行slave向master的ping操作,然后再次交由serverCron進行Pong的接收。這樣其他功能在每次功能塊執行結束后都能被執行,而不至於線程的時間都堵塞在某一個功能上。
 
REDIS epoll 講解
https://www.cnblogs.com/yuxingfirst/archive/2012/11/18/2776012.html
 
/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

建鏈流程:
2 initServer->aeCreateEventLoop->aeApiCreate  創建epoll實例  state->epfd = epoll_create(1024);
3.initServer->listenToPort->anetUnixServer->anetTcpServer 創建本地socket fd-1
4. initServer->aeCreateFileEvent->aeApiAddEvent->epoll_ctl(epfd,fd-1);把處理函數acceptTcpHandler注冊到wfileProc/rfileProc

5. main->acMain->aeProcessEvents->aeApiPoll->epoll_wait->如果有數據->wfileProc/rfileProc 回調到 acceptTcpHandler->accept,建鏈完成
建鏈完成后,會針對這個新的fd創建redis client結構
而在client的創建過程中,createClient會再次注冊一個event,回調函數readQueryFromClient,來進行從這個新socket的數據的接收


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM