咱們常常聽到說Redis是單線程的,也會有疑問:爲何單線程的Redis能那麼快?網絡
這裏要明白一點:Redis是單線程,主要是指Redis的網絡IO和鍵值對讀寫是由一個線程來完成的,這也是Redis對外提供鍵值存儲服務的主要流程。但Redis的其餘功能,好比持久化、異步刪除、集羣數據同步等,都是由額外的線程執行的。數據結構
咱們知道多線程可以提高併發性能,那爲何Redis會採用單線程,而非多線程?爲何單線程能那麼快?多線程
下面咱們就來學習一下Redis採用單線程的緣由。併發
使用多線程,雖然能夠增長系統吞吐率,或是增長系統擴展性,但一樣會產生開銷。異步
Redis的數據是在內存裏的,是共享的,若是使用多線程就會引起共享資源的競爭,須要引入互斥鎖來解決,使得並行變串行。最終系統吞吐率並無隨着線程的增長而增長。socket
另外,多線程開發須要精細的設計,會增長系統的複雜度,下降代碼的易調試性和可維護性。爲了不這些問題,Redis採用單線程模式。函數
一般來講,單線程的處理能力比多線程要差不少,那Redis卻能使用單線程模型達到每秒數十萬級別的處理能力,這是爲何呢?高併發
一方面,Redis大多數操做是在內存上完成的,而且採用高效的數據結構,例如哈希表和跳錶。另外一方面,Redis採用了多路複用機制,使其在網絡IO操做中能併發處理大量的客戶端請求,實現高吞吐率。性能
在學習多路複用機制前,咱們要弄明白網絡操做的基於IO模型和潛在的阻塞點。學習
以Get請求爲例,爲了處理一個Get請求:
下圖顯示了這一過程,其中,bind/listen、accept、recv、parse和send屬於網絡IO處理,而get屬性鍵值數據操做。
可是在這裏的網絡IO操做中,有潛在的阻塞點,分別是accept()和recv()。
這就致使Redis整個線程阻塞,沒法處理其餘客戶端請求,效率很低。不過,幸運的是,socket網絡模型自己支持非阻塞模式。
Socket網絡模型能夠設置非阻塞模式。
這樣能保證Redis線程既不會像基本IO模型中一直在阻塞點等待,也不會致使Redis沒法處理實際到達的鏈接請求或數據。
下面就到多路複用機制登場了。
Linux的IO多路複用機制是指一個線程處理多個IO流,也就是select/epoll機制。
在Redis運行單線程下,該機制容許內核中,同時存在多個監聽套接字和已鏈接套接字。
爲了在請求到達時能通知到Redis線程,select/epoll提供了基於事件的回調機制,即針對不一樣事件的發生,調用相應的處理函數。
回調機制的工做流程:
由於Redis一直在對事件隊列進行處理,因此能及時響應客戶端請求,提高Redis的響應性能。
不過,須要注意的是,在不一樣的操做系統上,多路複用機制也是適用的。
在「Redis基本IO模型」圖中,有哪些潛在的性能瓶頸?
Redis單線程處理IO請求性能瓶頸主要包括2個方面:
一、任意一個請求在server中一旦發生耗時,都會影響整個server的性能 也就是說後面的請求都要等前面這個耗時請求處理完成,本身才能被處理到。
耗時的操做包括:
解決辦法:
二、併發量很是大時,單線程讀寫客戶端IO數據存在性能瓶頸,雖然採用IO多路複用機制,可是讀寫客戶端數據依舊是同步IO,只能單線程依次讀取客戶端的數據,沒法利用到CPU多核。
解決辦法: