Chanler

Chanler

「黑馬 Redis 原理」二、網絡模型

使用者空間、核心空間#

image.png|500

IO 模型#

《UNIX 網路程式設計》 總結了五種 IO 模型

  • 阻塞 IO / Blocking IO
  • 非阻塞 IO / Nonblocking IO
  • IO 多路複用 / IO Multiplexing
  • 信號驅動 IO / Signal Driven IO
  • 非同步 IO / AsyncAsynchronous IO

使用者空間的使用者緩衝區向核心空間的核心緩衝區 等待資料就緒
硬體的硬體設備向核心空間的核心緩衝區 準備資料
使用者空間的使用者緩衝區向核心空間的核心緩衝區 讀取資料

阻塞 IO / Blocking IO#

image.png|500

非阻塞 IO / Nonblocking IO#

image.png|500

IO 多路複用 / IO Multiplexing#

無論是阻塞 IO 還是非阻塞 IO,使用者在一階段都需要 recvfrom 獲取資料,但都需要等待資料就緒,無非就是阻塞等待還是非阻塞等待,總之就只能處理這一個

但是如果同時監聽多個資料源,哪個資料源就緒就處理哪個資料源呢,這就是 IO 多路複用

image.png|500

在 Linux 中檔案描述符 file descriptor 可以關聯一切,這裡就可以是網路 socket,同時等待多個 fd,誰就緒處理誰

image.png|500

IO 多路複用有多種方案,如 select、poll、epoll,select、poll 只能知道 fd 集合中有 fd 就緒,不知道具體哪個

image.png|500

IO 多路複用 - select#

將 fd 轉成二進位標識位存儲,如監聽 fd=1 2 5,就是最右數第 1 2 5 位標記為 1,傳給核心空間

如果對應位的 fd 就緒則重記為 1,其餘則記 0,遍歷傳到使用者空間的類位圖,那個位是 1 則代表就緒

好處就是非常省空間;壞處就是不能超過 1024,且 fd_set 需要來回拷貝,並且檢查時還需要整個遍歷

image.png|500

IO 多路複用 - poll#

poll 提升了 select 1024 的限制,用鏈表存儲理論無上限,但沒必要,越長遍歷時越耗性能

image.png|500

IO 多路複用 - epoll#

epoll 採用紅黑樹,在核心空間中維護一個紅黑樹,記錄所有需要監聽的 fd;同時維護一個就緒列表,存放所有就緒的 fd

流程:使用者空間通過 epoll_create 創建 epoll 實例,通過 epoll_ctl 往紅黑樹中添加或刪除 fd,通過 epoll_wait 等待就緒事件,並從就緒列表中獲取就緒的 fd

當某個 fd 就緒時,核心會將其添加到就緒列表中。epoll_wait 只需要從就緒列表中取出就緒的 fd,不需要遍歷整個 fd 集合

好處:無 fd 上限,無需拷貝整個 fd 集合,只傳遞單個 fd 及操作,只返回就緒 fd,利用 ep_poll_callback 機制監聽 fd 狀態,無需遍歷所有 fd

image.png|500

IO 多路複用 - 事件通知機制#

當監聽的 fd 上發生事件(如資料可讀、可寫)時,核心會通過回調機制,如 epoll 的 ep_poll_callback 通知 epoll 實例

核心會將就緒的 fd 添加到 epoll 實例的就緒列表中。 應用程式調用 epoll_wait 時,就可以直接從就緒列表中獲取已發生事件的 fd 進行處理,而無需遍歷所有監聽的 fd

實際使用 ET 模式,即只有新資料才會通知,對於已通知的資料,循環進行非阻塞讀取,直到讀取完成

image.png|500

IO 多路複用 - web 服務流程#

image.png|500

信號驅動 IO#

信號驅動 IO 是一種非阻塞 IO 模型

應用程式可以設置一個信號處理函數,當核心在 fd 就緒時發送這個信號給進程,進程收到信號,立即執行 recvfrom 讀取資料

好處:非阻塞,無需主動輪詢

壞處:讀取資料仍然阻塞正常,信號處理有開銷,信號可能丟失,不如 IO 多路複用在高併發中

image.png|500

非同步 IO#

非同步 IO 在使用者發起 IO 操作後立即返回,沒有任何阻塞階段

核心不僅負責等待資料就緒,還要負責將資料從核心空間拷貝到使用者空間,整個 IO 操作完成之後,核心才會根據使用者註冊的回調函數或信號通知使用者進程

好處:使用者進程在發起 IO 請求到接收到完成通知期間,完全非阻塞

壞處:複雜

image.png|500

同步與非同步#

非同步 IO 在二階段也就是讀取資料階段也是非同步的

image.png|500

Redis 是單執行緒的嗎?#

Redis 到底是單執行緒還是多執行緒?

  • Redis 的核心業務部分 命令處理,是單執行緒
  • 整個 Redis,是多執行緒

在 Redis 版本選代過程中,在兩個重要的時間節點上引入了多執行緒的支持:

  • Redis V4.0:引入多執行緒非同步處理一些耗時較舊的任務,例如非同步刪除命令 unlink
  • Redis v6.0:在核心網路模型中引入多執行緒,進一步提高對於多核 CPU 的利用率

對於 Redis 核心網路模型,在 Redis v6.0 之前都是單執行緒,利用 epoll 這樣的 IO 多路複用技術在事件循環中不斷處理客戶端的情況

Redis 為什麼選擇單執行緒?

  • Redis 在命令處理上純內存操作,本身執行速度就很快了,性能瓶頸在於網路 IO 而非執行速度,因此專注於網路 IO 優化比多執行緒更有必要
  • 多執行緒會導致過多的上下文切換,反而帶來不必要的開銷
  • 引入多執行緒還會存在線程安全問題,還需引入線程鎖這類安全手段,反而浪費性能

Redis 實現的網路模型#

image.png|500

image.png|500

|500

image.png|500

image.png|500

此文由 Mix Space 同步更新至 xLog
原始連結為 https://blog.0xling.cyou/posts/redis/redis-4


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。