天天看點

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

一. Redis6.0 新特性

1. 多線程IO

 redis6.0引入多線程IO,隻是用來處理網絡資料的讀寫和協定的解析,而執行指令依舊是單線程,是以不需要去考慮set/get、事務、lua等的并發問題。(詳細的線程模型見後面)

 多線程IO的性能提升測試可參考:https://zhuanlan.zhihu.com/p/76788470  (相對權威)    自己測試:https://www.cnblogs.com/yaopengfei/p/13922295.html

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)
第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

2.  ACL精細化權限控制

 在Redis 5版本之前,Redis 安全規則隻有密碼控制 還有通過rename 來調整高危指令比如

flushdb

KEYS*

shutdown

等。

 Redis 6 則提供ACL的功能對使用者進行更細粒度的權限控制 ,支援對用戶端的權限控制,實作對不同的key授予不同的操作權限:

(1)接入權限: 使用者名和密碼 (2)可以執行的指令 (3)可以操作的 KEY

相關指令如下:

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

3. 支援RESP3

 RESP(Redis Serialization Protocol)是 Redis 服務端與用戶端之間通信的協定。

 Redis 5 使用的是 RESP2,而 Redis 6 開始在相容 RESP2 的基礎上,開始支援 RESP3。推出RESP3的目的:一是因為希望能為用戶端提供更多的語義化響應,以開發使用舊協定難以實作的功能;另一個原因是實作 Client-side-caching(用戶端緩存)功能。

4. 重新設計了用戶端緩存

  基于 RESP3 協定實作的用戶端緩存功能。為了進一步提升緩存的性能,将用戶端經常通路的資料cache到用戶端,減少TCP網絡互動,提升RT。

5. 提升了RDB的加載速度

  根據檔案的實際組成(較大或較小的值),可以預期20/30%的改進。當有很多客戶機連接配接時,資訊也更快了,這是一個老問題,現在終于解決了。

6. 支援SSL  

 連接配接支援SSL,更加安全。

7. Redis Cluster proxy(叢集代理)

  antirez開發了 Proxy 功能,讓 Cluster 擁有像單執行個體一樣的接入方式,降低大家使用cluster的門檻。不過需要注意的是代理不改變 Cluster 的功能限制,不支援的指令還是不會支援,比如跨 slot 的多Key操作。

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

8. Modules API

 Redis 6中子產品API開發進展非常大,因為Redis Labs為了開發複雜的功能,從一開始就用上Redis子產品。Redis可以變成一個架構,利用Modules來建構不同系統,而不需要從頭開始寫然後還要BSD許可。Redis一開始就是一個向編寫各種系統開放的平台。

 Disque作為一個Redis Module使用足以展示Redis的子產品系統的強大。叢集消息總線API、屏蔽和回複用戶端、計時器、子產品資料的AOF和RDB等等。

參考:http://antirez.com/latest/0  (redis作者)

二. 剖析線程模型

1. 靈魂拷問

(1). 為什麼說redis6.0之前是單線程的?

 redis執行用戶端指令的請求從:  擷取 (socket 讀)→解析→執行→内容傳回 (socket 寫) 等等都是由一個線程處理,所有操作是一個個挨着串行執行的 (主線程),這就是稱redis是單線程的原因。

但是:redis從4.0開始,也有背景線程在工作,處理一些較為緩慢的操作,例如無用連接配接的釋放、大 key 的删除等等,嚴格意義上來說,redis6.0之前也不完全是單線程的。

(2). 單線程非常快的原因是什麼?

 A. 純記憶體操作,避免大量通路資料庫,減少直接讀取磁盤資料,redis将資料儲存在記憶體裡面,讀寫資料的時候都不會受到硬碟 I/O 速度的限制,是以速度快.

 B. 單線程操作,避免了不必要的上下文切換和競争條件,也不存在多程序或者多線程導緻的切換而消耗CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導緻的性能消耗.

 C. 采用了非阻塞I/O 多路複用機制.

(3). redis在6.0之前為什麼一直堅持單線程?

 官方曾做過類似問題的回複:使用Redis時,幾乎不存在CPU成為瓶頸的情況, Redis主要受限于記憶體和網絡。例如在一個普通的Linux系統上,Redis通過使用pipelining每秒可以處理100萬個請求,是以如果應用程式主要使用O(N)或O(log(N))的指令,它幾乎不會占用太多CPU。

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

(4). redis6.0引入多線程機制的背景是什麼? 

 從Redis自身角度來說,因為讀寫網絡的read/write系統調用占用了Redis執行期間大部分CPU時間,瓶頸主要在于網絡的 IO 消耗, 優化主要有兩個方向:

  (1). 提高網絡 IO 性能,典型的實作比如使用 DPDK 來替代核心網絡棧的方式

  (2). 使用多線程充分利用多核,典型的實作比如 Memcached。

 協定棧優化的這種方式跟 Redis 關系不大,支援多線程是一種最有效最便捷的操作方式。是以總結起來,redis支援多線程主要就是兩個原因:

  (1). 可以充分利用伺服器 CPU 資源,目前主線程隻能利用一個核

  (2). 多線程任務可以分攤 Redis 同步 IO 讀寫負荷

2. redis單線程模型(6.0之前)

 Redis用戶端對服務端的每次調用都經曆了發送指令,執行指令,傳回結果三個過程。其中執行指令階段,由于Redis是單線程來處理指令的,所有每一條到達服務端的指令不會立刻執行,所有的指令都會進入一個隊列中,然後逐個被執行。并且多個用戶端發送的指令的執行順序是不确定的。但是可以确定的是不會有兩條指令被同時執行,不會産生并發問題,這就是Redis的單線程基本模型。

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)
PS:可以修改redis的最大連結數,預設為10000,如下圖,如果要修改的話,直接修改配置檔案重點maxclients即可。
第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

(1). 什麼是非阻塞IO?

 非阻塞 IO 在 Socket 對象上提供了一個選項

Non_Blocking

 ,當這個選項打開時,讀寫方法不會阻塞,而是能讀多少讀多少,能寫多少寫多少。

 能讀多少取決于核心為 Socket 配置設定的讀緩沖區的大小,能寫多少取決于核心為 Socket 配置設定的寫緩沖區的剩餘空間大小。讀方法和寫方法都會通過傳回值來告知程式實際讀寫了多少位元組資料。

 有了非阻塞 IO 意味着線程在讀寫 IO 時可以不必再阻塞了,讀寫可以瞬間完成然後線程可以繼續幹别的事了。

補充阻塞IO概念:

 當我們調用 Scoket 的讀寫方法,預設它們是阻塞的。

 read() 方法要傳遞進去一個參數 n,表示讀取這麼多位元組後再傳回,如果沒有讀夠 n 位元組線程就會阻塞,直到新的資料到來或者連接配接關閉了, read 方法才可以傳回,線程才能繼續處理。

 write() 方法會首先把資料寫到系統核心為 Scoket 配置設定的寫緩沖區中,當寫緩存區滿溢,即寫緩存區中的資料還沒有寫入到磁盤,就有新的資料要寫道寫緩存區時,write() 方法就會阻塞,直到寫緩存區中有空閑空間。

(2). 什麼是IO多路複用?

背景:

 非阻塞 IO 有個問題,那就是單個線程要處理多個讀寫請求,處理某個用戶端的的讀資料的請求,結果讀了一部分就傳回了,線程如何知道什麼時候才應該繼續讀資料。處理寫請求的時候,如果緩沖區滿了,寫不完,剩下的資料何時才應該繼續寫?在什麼時候處理什麼請求?redis 單線程處理多個IO請求時就用到了IO多路複用技術。

原理:

 如下圖,redis 需要處理 3 個 IO 請求,同時把 3 個請求的結果傳回給用戶端,是以總共需要處理 6 個 IO 事件,由于 redis 是單線程模型,同一時間隻能處理一個 IO 事件,于是 redis 需要在合适的時間暫停對某個 IO 事件的處理,轉而去處理另一個 IO 事件,這樣 redis 就好比一個開關,當開關撥到哪個 IO 事件這個電路上,就處理哪個 IO 事件,其他 IO 事件就暫停處理了。這就是IO多路複用技術。

 以上是大緻的了解下 IO 多路複用技術,在系統底層,IO 多路複用有 3 種實作機制:select、poll、epoll。

第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

(3). 什麼是檔案處理器?

 A. Redis 基于 Reactor 模式開發了自己的網絡事件處理器: 這個處理器被稱為檔案事件處理器(file event handler)

 B. 檔案事件處理器使用 I/O 多路複用(multiplexing)程式來同時監聽多個套接字, 并根據套接字目前執行的任務來為套接字關聯不同的事件處理器。

 C. 當被監聽的套接字準備好執行連接配接應答(accept)、讀取(read)、寫入(write)、關閉(close)等操作時, 與操作相對應的檔案事件就會産生, 這時檔案事件處理器就會調用套接字之前關聯好的事件處理器來處理這些事件。

 D. 檔案事件處理器以單線程方式運作, 但通過使用 I/O 多路複用程式來監聽多個套接字, 檔案事件處理器既實作了高性能的網絡通信模型, 又可以很好地與 redis 伺服器中其他同樣以單線程方式運作的子產品進行對接, 這保持了 Redis 内部單線程設計的簡單性。

3. redis多線程模型(6.0開始)

(1). 流程如下:(如下圖)

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

(2). 特點如下:

  • IO 線程要麼同時在讀 socket,要麼同時在寫,不會同時讀或寫
  • IO 線程隻負責讀寫 socket 解析指令,不負責指令處理(主線程串行執行指令)
  • IO 線程數可自行配置
第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)
第十一節:Redis6.0新特性、剖析線程模型(單線程和多線程)

 參考:https://ruby-china.org/topics/38957%EF%BC%89

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 部落格位址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎讨論,請勿謾罵^_^。
  • 聲     明2 : 原創部落格請在轉載時保留原文連結或在文章開頭加上本人部落格位址,否則保留追究法律責任的權利。