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等待到了值,則返回給客戶端。如下圖所示:

3. 總結

  1. Pipeline減少了RTT,也減少了IO調用次數(IO調用涉及到用戶態到內核態之間的切換)
  2. 需要控制Pipeline的大小,否則會消耗Redis的內存
  3. Codis 3.2 Pipeline默認10K,3.1則是1024
    Jedis客戶端緩存是8192,超過該大小則刷新緩存,或者直接發送

4. 參考資料

  1. Redis官方文檔:https://redis.io/topics/pipelining
  2. Redis 協議
  3. Redis源碼(請求處理響應部分)
  4. Codis源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM