摘抄自《redis深度歷險》。
Redis是個高並發的中間件,但是確實是單線程。而且,Nginx、Node.js等也是單線程的。Redis通過非阻塞IO(IO多路復用)處理那么多的並發客戶端連接,並且,由於Redis所有的數據都在內存中,其所有的操作都是內存級別,因此速度非常快。另一方面,由於Redis是單線程,所以要小心使用Redis的一些指令,尤其是一些復雜度為O(n)的指令,一不小心就會導致Redis卡頓。
非阻塞IO
linux下非阻塞IO方式有select、epoll等等,關於epoll如何使用可以看這篇博客:《Linux下socket通信和epoll》。
非阻塞IO的偽代碼一般如下:
如果沒有任何事件到來,那么線程最多等待timeout時間,此時線程阻塞。一旦有事件到來,就可以立即返回,timeout時間到了仍然沒有事件到來,也立即返回。拿到事件后,線程可以繼續挨個處理相應的事件。處理完了,再重新輪詢。這個循環一般稱為事件循環。
指令隊列
Redis會將每個客戶端套接字都關聯一個指令隊列,客戶端的指令通過隊列來進行順序處理,先到先服務。
響應隊列
Redis同樣為每個客戶端套接字關聯一個響應隊列。Redis通過響應隊列來將指令的返回結果回復給客戶端。
定時任務
服務器除了響應IO事件外,還要處理其他任務,比如定時任務。但是當線程處於一個事件循環中,處於阻塞狀態,怎樣准確調度定時任務呢?
Redis會將定時任務記錄在最小堆中,最近的一個定時任務將在堆的最上方。每個事件循環周期Redis線程都會執行當前的定時任務,在進入下一個事件循環時,將記錄下一個最近的定時任務時間,並且將時間差設置為timeout時間。這樣子Redis就知道,在timeout時間內沒有其他定時任務要做,因此可以安心的阻塞在事件循環中。
Nginx和Node.js的事件處理原理和Redis類似。