一、Redis為什麼是單執行緒
注意:redis 單執行緒指的是網路請求模組使用了一個執行緒,即一個執行緒處理所有網路請求,其他模組仍用了多個執行緒。
因為CPU不是Redis的瓶頸。Redis的瓶頸最有可能是機器記憶體或者網路頻寬,既然單執行緒容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單執行緒的方案了。關於redis的效能,官方網站也有,普通筆記本輕鬆處理每秒幾十萬的請求。
二、Redis為什麼這麼快
1、完全基於記憶體,絕大部分請求是純粹的記憶體操作,非常快速。資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1);
2、資料結構簡單,對資料操作也簡單,Redis中的資料結構是專門進行設計的;
3、採用單執行緒,避免了不必要的上下文切換和競爭條件,也不存在多程式或者多執行緒導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的效能消耗;
4、使用多路I/O複用模型,非阻塞IO;
5、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通訊的應用協議不一樣,Redis直接自己構建了VM 機制 ,因為一般的系統呼叫系統函式的話,會浪費一定的時間去移動和請求;
三、多路I/O複用模型,非阻塞IO
下面舉一個例子,模擬一個tcp伺服器處理30個客戶socket。 假設你是一個監考老師,讓30個學生解答一道競賽考題,然後負責驗收學生答卷,你有下面幾個選擇:
- 第一種選擇:按順序逐個驗收,先驗收A,然後是B,之後是C、D。。。這中間如果有一個學生卡住,全班都會被耽誤。 這種模式就好比,你用迴圈挨個處理socket,根本不具有併發能力。
- 第二種選擇:你建立30個分身,每個分身檢查一個學生的答案是否正確。 這種類似於為每一個使用者建立一個程式或者執行緒處理連線。
- 第三種選擇,你站在講臺上等,誰解答完誰舉手。這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然後繼續回到講臺上等。此時E、A又舉手,然後去處理E和A。。。 這種就是IO複用模型,Linux下的select、poll和epoll就是幹這個的。將使用者socket對應的fd註冊進epoll,然後epoll幫你監聽哪些socket上有訊息到達,這樣就避免了大量的無用操作。此時的socket應該採用非阻塞模式。 這樣,整個過程只在呼叫select、poll、epoll這些呼叫的時候才會阻塞,收發客戶訊息是不會阻塞的,整個程式或者執行緒就被充分利用起來,這就是事件驅動,所謂的reactor模式。
針對上面的舉例在Redis中表現為
有30個redis客戶端(考生)與redis伺服器的網路連線模組(監考老師)保持TCP連線,客戶端會不定時的傳送請求給伺服器,當有一個redis客戶端發起請求,會觸發unix系統像epoll這樣的系統呼叫,Redis的I/O 多路複用模組封裝了底層的epoll這樣的 I/O 多路複用函式,然後轉發到相應的事件處理器。
檔案事件處理器使用 I/O 多路複用模組同時監聽多個 FD(檔案描述符),當 accept、read、write 和 close 檔案事件產生時,檔案事件處理器就會回撥 FD 繫結的事件處理器。
雖然整個檔案事件處理器是在單執行緒上執行的,但是通過 I/O 多路複用模組的引入,實現了同時對多個 FD 讀寫的監控,提高了網路通訊模型的效能,同時也可以保證整個 Redis 服務實現的簡單。