Redis Pipeline原理分析

出處:http://www.cnblogs.com/jabnih/

1. 基本原理

1.1 爲什麼會出現Pipeline

Redis本身是基於Request/Response協議的,正常情況下,客戶端發送一個命令,等待Redis應答,Redis在接收到命令,處理後應答。在這種情況下,如果同時需要執行大量的命令,那就是等待上一條命令應答後再執行,這中間不僅僅多了RTT(Round Time Trip),而且還頻繁的調用系統IO,發送網絡請求。如下圖。

爲了提升效率,這時候Pipeline出現了,它允許客戶端可以一次發送多條命令,而不等待上一條命令執行的結果,這和網絡的Nagel算法有點像(TCP_NODELAY選項)。不僅減少了RTT,同時也減少了IO調用次數(IO調用涉及到用戶態到內核態之間的切換)。如下圖:

客戶端這邊首先將執行的命令寫入到緩衝中,最後再一次性發送Redis。但是有一種情況就是,緩衝區的大小是有限制的,比如Jedis,限制爲8192,超過了,則刷緩存,發送到Redis,但是不去處理Redis的應答,如上圖所示那樣。

1.2 實現原理

要支持Pipeline,其實既要服務端的支持,也要客戶端支持。對於服務端來說,所需要的是能夠處理一個客戶端通過同一個TCP連接發來的多個命令,可以理解爲,這裏將多個命令切分,和處理單個命令一樣(之前老生常談的黏包現象),Redis就是這樣處理的。而客戶端,則是要將多個命令緩存起來,緩衝區滿了就發送,然後再寫緩衝,最後才處理Redis的應答,如Jedis。

1.3 從哪個方面提升性能

正如上面所說的,一個是RTT,節省往返時間,但是另一個原因也很重要,就是IO系統調用。一個read系統調用,需要從用戶態,切換到內核態。

1.4 注意點

Redis的Pipeline和Transaction不同,Transaction會存儲客戶端的命令,最後一次性執行,而Pipeline則是處理一條,響應一條,但是這裏卻有一點,就是客戶端會並不會調用read去讀取socket裏面的緩衝數據,這也就造就了,如果Redis應答的數據填滿了該接收緩衝(SO_RECVBUF),那麼客戶端會通過ACK,WIN=0(接收窗口)來控制服務端不能再發送數據,那樣子,數據就會緩衝在Redis的客戶端應答列表裏面。所以需要注意控制Pipeline的大小。如下圖:

2. Codis Pipeline

在一般情況下,都會在Redis前面使用一個代理,來作負載以及高可用。這裏在公司裏面使用的是Codis,以Codis 3.2版本爲例(3.2版本是支持Pipeline的)。

Codis在接收到客戶端請求後,首先根據Key來計算出一個hash,映射到對應slots,然後轉發請求到slots對應的Redis。在這過程中,一個客戶端的多個請求,有可能會對應多個Redis,這個時候就需要保證請求的有序性(不能亂序),Codis採用了一個Tasks隊列,將請求依次放入隊列,然後loopWriter從裏面取,如果Task請求沒有應答,則等待(這裏和Java的Future是類似的)。內部BackenRedis是通過channel來進行通信的,dispatcher將Request通過channel發送到BackenRedis,然後BackenRedis處理完該請求,則將值填充到該Request裏面。最後loopWriter等待到了值,則返回給客戶端。如下圖所示:


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