Redis爲什麼這麼快?一文帶你深入理解Redis單線程分析

redis爲什麼那麼快?結論有三點,大家都知道,這裏主要是分析。

首先第一點

redis是內存訪問的,所以快

當然這個大家都知道,所以不是重點

 

IO密集型和CPU密集型

一般我們把任務分爲io密集型cpu密集型

 

IO密集型

 

  • IO密集型指的是系統的CPU性能相對硬盤、內存要好很多,此時,系統運作,大部分的狀況是CPU在等I/O (硬盤/內存) 的讀/寫操作,此時CPU Loading並不高。
  • 對於io密集型的任務,它的主要時間都在磁盤io上,而io本身在發出中斷告知cpu後,cpu只需要短暫的處理一下,之後就由DMA(詳見附錄)負責數據傳輸,整個過程對cpu的利用率很低。因此我們需要開更多的線程去充分利用cpu。即一般線程數 = cpu核心數 * 2,如數據庫連接池

 

CPU密集型

 

  • CPU密集型也叫計算密集型,指的是系統的硬盤、內存性能相對CPU要好很多,此時,系統運作大部分的狀況是CPU Loading 100%,CPU要讀/寫I/O(硬盤/內存),I/O在很短的時間就可以完成,而CPU還有許多運算要處理,CPU Loading很高。
  • 對於cpu密集型的任務,它對cpu的利用率很高,所以不需要開更多的線程去提高cpu利用率。假如增加線程,只會引起線程的頻繁切換導致本來就不夠用的cpu更加不夠用。所以一般是線程數 = cpu核心數 + 1

 

Redis的瓶頸在哪裏

 

redis基本都在進行內存io,那它的瓶頸在io上嗎?

redis在網絡io上使用epoll實現了一個io多路複用的reactor模型,epoll是非阻塞io,所以避免了cpu阻塞在io上,所以它不是io密集型,瓶頸不在於等待io導致cpu利用率不高,不需要多個線程來屏蔽等待io執行完成的時間。當然redis的io利用率很高,但是io利用率高並不代表它是io密集型,因爲它瓶頸不在等待io上。

所以第二點

redis在網絡io上使用epoll實現了一個io多路複用的reactor模型使得cpu利用率更高,浪費在io上的時間更少

redis並不需要多線程來提高cpu利用率減少io等待時間,並且單線程架構也比較容易實現,所以順理成章就採用了單線程架構。

第三點

由於採用了單線程架構,避免了線程線程切換產生的消耗

因爲一次CPU上下文的切換大概在 1500ns 左右。

從內存中讀取 1MB 的連續數據,耗時大約爲 250us,假設1MB的數據由多個線程讀取了1000次,那麼就有1000次時間上下文的切換,

那麼就有1500ns * 1000 = 1500us ,我單線程的讀完1MB數據才250us ,你光時間上下文的切換就用了1500us了,我還不算你每次讀一點數據 的時間

那麼redis是cpu密集型嗎?答案是否定的。

redis也不是cpu密集型。大多數情況下redis機器上的cpu是很夠用的。

redis的瓶頸在於內存大小和網絡帶寬。

如果想要更充分的利用多核cpu,可以採用多個redis實例的方法,同時爲了減少線程爭用,可以將實例和cpu綁定的方法。

但是如果做了CPU綁定,在rdb和aof時子進程會與父進程共享使用一個CPU。子進程重寫時對單核CPU使用率通常在90%以上,父進程與子進程將產生激烈CPU競爭,極大影響Redis穩定性。(解決方法不清楚,也許多綁定一個CPU會好點?)

附錄

DMA

DMA 傳輸將數據從一個地址空間複製到另外一個地址空間。當CPU 初始化這個傳輸動作,傳輸動作本身是由 DMA 控制器來實行和完成。

典型的例子就是移動一個外部內存的區塊到芯片內部更快的內存區。例如磁盤移到內存。

客戶端與Redis通信的一次流程

解釋說明

①.在Redis啓動及初始化的時候,Redis會(預先)將連接應答處理器跟"AE_READABLE"事件關聯起來,接着如果一個客戶端向Redis發起連接,此時就會產生一個"AE_READABLE"事件,然後由連接應答處理器來處理跟客戶端建立連接,創建客戶端對應的socket,同時將這個socket的"AE_READABLE"事件跟命令請求處理器關聯起來;

②.當客戶端向Redis發起請求的時候(不管是讀請求還是寫請求,都一樣),首先就會在之前創建的客戶端對應的socket上產生一個"AE_READABLE"事件,然後IO多路複用程序會監聽到在之前創建的客戶端對應的socket上產生了一個"AE_READABLE"事件,接着把這個socket放入一個隊列中排隊,然後由文件事件分派器從隊列中獲取socket交給對應的命令請求處理器來處理(因爲之前在Redis啓動並進行初始化的時候就已經預先將"AE_READABLE"事件跟命令請求處理器關聯起來了).之後命令請求處理器就會從之前創建的客戶端對應的socket中讀取請求相關的數據,然後在自己的內存中進行執行和處理;

③.當客戶端請求處理完成,Redis這邊也準備好了給客戶端的響應數據之後,就會(預先)將socket的"AE_WRITABLE"事件跟命令回覆處理器關聯起來,當客戶端這邊準備好讀取響應數據時,就會在之前創建的客戶端對應的socket上產生一個"AE_WRITABLE"事件,然後IO多路複用程序會監聽到在之前創建的客戶端對應的socket上產生了一個"AE_WRITABLE"事件,接着把這個socket放入一個隊列中排隊,然後由文件事件分派器從隊列中獲取socket交給對應的命令回覆處理器來處理(因爲之前在Redis這邊準備好給客戶端的響應數據之後就已經預先將"AE_WRITABLE"事件跟命令回覆處理器關聯起來了),之後命令回覆處理器就會向之前創建的客戶端對應的socket輸出/寫入準備好的響應數據,最終返回給客戶端,供客戶端來讀取;

④.當命令回覆處理器將準備好的響應數據寫完之後,就會刪除之前創建的客戶端對應的socket上的"AE_WRITABLE"事件和命令回覆處理器的關聯關係;

綜上所述,爲什麼Redis單線程模型也能效率這麼高?

  1. 純內存操作;
  2. 核心是基於非阻塞的IO多路複用機制;
  3. 底層使用C語言實現,一般來說,C 語言實現的程序"距離"操作系統更近,執行速度相對會更快;
  4. 單線程同時也避免了多線程的上下文頻繁切換問題,預防了多線程可能產生的競爭問題;

喜歡小編請多多點贊評論轉發,關注小編,你們的支持就是小編最大的動力~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章