首頁  >  文章  >  資料庫  >  淺析Redis6中的單執行緒與多執行緒模型

淺析Redis6中的單執行緒與多執行緒模型

青灯夜游
青灯夜游轉載
2022-01-26 10:38:023262瀏覽

這篇文章帶大家了解Redis6中的執行緒模型,介紹一下單執行緒模型和多執行緒模型,希望對大家有幫助!

淺析Redis6中的單執行緒與多執行緒模型

1. Redis演進史

#如果單純的說redis是單線程或多線程,這個回答肯定不嚴謹,不同版本使用的線程模型是不一樣的。 【相關推薦:Redis影片教學

淺析Redis6中的單執行緒與多執行緒模型

  • #版本3.x ,最早版本,也就是大家口耳相傳的redis是單線程。

  • 版本4.x,嚴格意義來說也不是單線程,而是負責處理客戶端請求的線程是單線程,但是開始加了點多線程的東西(異步刪除)

  • 最新版本的6.0.x後, 告別了大家印像中的單線程,用一種全新的多線程來解決問題。

2. Redis單執行緒模型

#2.1 單執行緒真實意義

主要是指Redis的網路IO和鍵值對讀寫是由一個執行緒來完成的,Redis在處理客戶端的請求時包括取得(socket 讀)、解析、執行、內容回傳(socket 寫) 等都由一個順序串行的主線程處理,這就是所謂的“單線程”。這也是Redis對外提供鍵值儲存服務的主要流程。 淺析Redis6中的單執行緒與多執行緒模型
但Redis的其他功能, 例如持久化、非同步刪除、叢集資料同步等等,其實是由額外的執行緒執行的。  可以這麼說,Redis工作執行緒是單執行緒的。但是,整個Redis來說,是多執行緒的

2.2 單執行緒效能快原因

##Redis 3 .x 單執行緒時代但是

效能很快的主要原因

    基於記憶體運算:所有資料都存在記憶體中,因此所有的運算都是記憶體等級的
  • 資料結構簡單:Redis的資料結構是專門設計的,而這些簡單的資料結構的查找和操作的時間大部分複雜度都是
  • o(1)
  • #多路復用和費阻塞IO:使用IO多路復用功能監聽多個socket連接客戶端,這樣就可以使用一個線程連接處理多個請求,減少線程切換帶來的開銷,同時避免IO阻塞操作
  • 避免上下文切換:因為是單執行緒模型,就可以避免不必要的上先文切換和多執行緒競爭,這樣可以省去多執行緒切換帶來的時間和效能上的消耗,而且單線程不會導致死鎖問題的發生

2.3 採用單線程原因

Redis 是基於記憶體操作的, 因此他的瓶頸可能是

機器的記憶體或網路頻寬而非CPU ,既然CPU 不是瓶頸,那麼自然就採用單執行緒的解決方案了,況且使用多執行緒比較麻煩。但是在 Redis 4.0 中開始支援多執行緒了,例如後台刪除等功能簡單來說,Redis  4.0 之前一直採用單線程的主要原因有以下三個: 

  • 使用單線程模型是Redis 的開發和維護更簡單,因為單線程模型方便開發和調試;多執行緒模型雖然在某些方面表現優異,但是它卻引入了程式執行順序的不確定性,帶來了並發讀寫的一系列問題,增加了系統複雜度、同時可能存在執行緒切換、甚至加鎖解鎖、死鎖造成的效能損耗。 Redis透過AE事件模型以及IO多路復用等技術,處理效能非常高,因此沒有必要使用多執行緒。單線程機制使得 Redis 內部實現的複雜度大大降低,Hash 的惰性 Rehash、Lpush 等等 “線程不安全” 的命令都可以無鎖進行。

  • 即使使用單一執行緒模型也並發的處理多客戶端的請求,主要使用的是多路復用和非阻塞IO; 

  • 對於Redis 系統來說,主要的

    效能瓶頸是記憶體或網路頻寬而非CPU

3. Redis多執行緒模型

#3.1 引入多執行緒原因

既然單執行緒那麼好,為啥又要引入多執行緒? 單一線程也有自己的煩惱,例如大key刪除問題: 正常情況下使用del 指令可以很快的刪除數據,而當被刪除的key 是一個非常大的物件時,例如時包含了成千上萬個元素的hash 集合時,那麼del 指令就會造成Redis 主線程卡頓。

因此,在 Redis 4.0 中就新增了多執行緒的模組,當然此版本中的多執行緒主要是為了解決刪除資料效率比較低的問題。可以透過惰性刪除有效避免Redis卡頓問題(大key刪除等問題),步驟如下:

  • ##unlink key : 與DEL一樣刪除key功能的lazy free實作,唯一不同是,UNLINK在刪除集合類型鍵時,如果集合鍵的元素個數大於64個,主執行緒中只是把待刪除鍵從資料庫字典中摘除,會把真正的記憶體釋放操作,給單獨的bio來操作。如果元素個數較少(少於64個)或是String類型,也會在主執行緒直接刪除。

  • flushall/flushdb async : 對於清空資料庫指令flushall/flushdb,新增了async非同步清理選項,使得redis在清空資料庫時都是非同步操作。實作邏輯是為資料庫新建一個新的空的字典,把原有舊的資料庫字典給後台執行緒來逐一刪除其中的數據,釋放記憶體。

  • 把刪除工作交給了後台子程序異步刪除資料

#因為Redis是單一主執行緒處理,redis之父antirez一直強調"Lazy Redis is better Redis". 而

lazy free的本質就是把某些cost(主要時間複製度,佔用主線程cpu時間片)較高刪除操作, 從redis主線程剝離讓bio子線程來處理,大大減少主線阻塞時間。從而減少刪除導致效能和穩定性問題。

Redis 4.0 就引入了多個線程來實現資料的非同步惰性刪除等功能,但是其處理讀寫請求的仍然只有一個線程,所以仍然算是狹義上的單線程。

從上一小結分析:Redis的主要效能瓶頸是記憶體或網路頻寬而非CPU。記憶體問題比較好解決,因此Redis的瓶頸原因為網路IO。接下來,介紹多執行緒模型。

3.2 多執行緒工作原理

I/O 的讀取和寫入本身是堵塞的,例如當socket 中有資料時,Redis會透過調用先將資料從內核態空間拷貝到用戶態空間,再交給Redis 調用,而這個拷貝的過程就是阻塞的,當資料量越大時拷貝所需的時間就越多,而這些操作都是基於單線程完成的。

淺析Redis6中的單執行緒與多執行緒模型

在Redis 6.0 中新增了

多執行緒的功能來提高I/O 的讀寫效能,他的主要實作思路是將主執行緒的IO 讀寫任務拆分給一組獨立的線程去執行,這樣就可以使多個socket 的讀寫可以並行化了,採用多路I/O 復用技術可以讓單個線程高效的處理多個連接請求(盡量減少網絡IO的時間消耗),將最耗時的Socket的讀取、請求解析、寫入單獨外包出去,剩下的命令執行仍然由主線程串行執行併和內存的數據交互。 淺析Redis6中的單執行緒與多執行緒模型結合上圖可知,
將網路資料讀寫、請求協定解析通過多個IO線程的來處理,對於真正的命令執行來說,仍然使用主線程操作(線程安全),是個不錯的折衷辦法。因此,對於整個Redis來說是多執行緒的,但是對於工作執行緒(指令執行)仍舊是單執行緒

3.3 工作流程

核心流程大概如下:

淺析Redis6中的單執行緒與多執行緒模型流程簡述如下:

    主執行緒取得socket 放入等待清單
  • 將socket 分配給各個IO 線程(並不會等列表滿)
  • 主線程
  • 阻塞等待IO 線程(多線程)讀取socket 完畢
  • #主執行緒執行指令-
  • 單執行緒(如果指令沒有接收完畢,會等IO 下次繼續)
  • 主執行緒
  • 阻塞等待IO 執行緒(多執行緒)將資料回寫socket 完畢(一次沒寫完,會等下次再寫)
  • 解除綁定,清空等待佇列

特點如下:

  • IO 執行緒要嘛同時在讀socket,要嘛同時在寫,不會同時讀或寫
  • IO 執行緒只負責讀寫socket 解析指令,不負責指令處理(主執行緒串列執行指令)
  • IO 執行緒數可自行配置(目前程式碼限制上限為512,預設為1(關閉此功能))

經過有心人士的壓測,目前效能能提升1 倍以上

疑問1:等待清單不滿 一直阻塞不處理嗎?
回覆:淺析Redis6中的單執行緒與多執行緒模型阻塞時偵測的是,IO 執行緒是否還有任務。等處理完了才繼續往下。這些任務是在執行時加上的,如果 任務數這塊還是有點疑問,哪位大佬可以解釋下(評論哈)?

3.4 預設開啟多執行緒嗎?

在Redis6.0中, 多執行緒機制預設是關閉的 ,如果需要使用多執行緒功能,則需要在redis.conf中完成兩個設定。 淺析Redis6中的單執行緒與多執行緒模型

  • 設定io-thread-do-reads設定項目為yes,表示啟動多執行緒。
  • 設定線程個數。關於線程數的設置,官方的建議是如果為4 核的CPU,建議線程數設置為2 或3,如果為8 核CPU 建議線程數設置為6 ,則線程數一定要小於機器核數,線程數就不是越大越好。

4. 總結

Redis本身出道就是優秀,基於記憶體運算、資料結構簡單、多重化與非阻塞I/O、避免了不必要的線程上下文切換等特性,在單線程的環境下仍然很快;

但對於大數據的key 刪除還是卡頓厲害,因此在Redis 4.0 引入了多線程unlink key/flushall async 等指令,主要用於Redis 資料的非同步刪除;

而在Redis 6.0 中引入了I/O 多執行緒的讀寫,這樣就可以更有效率的處理更多的任務了, Redis 只是將I/O 讀寫變成了多執行緒 ,而指令的執行依舊是由主執行緒串列執行的 ,因此在多執行緒下操作Redis 不會出現線程安全的問題。 

Redis 無論是當初的單線程設計,還是如今與當初設計相背的多線程,目的只有一個:讓 Redis 變得越來越快。

更多程式相關知識,請造訪:程式設計入門! !

以上是淺析Redis6中的單執行緒與多執行緒模型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除