轉(zhuǎn)載請注明出處:http://www.cnblogs.com/jabnih/

1. 基本原理

1.1 為什么會出現(xiàn)Pipeline

Redis本身是基于Request/Response協(xié)議的,正常情況下,客戶端發(fā)送一個命令,等待Redis應(yīng)答,Redis在接收到命令,處理后應(yīng)答。在這種情況下,如果同時需要執(zhí)行大量的命令,那就是等待上一條命令應(yīng)答后再執(zhí)行,這中間不僅僅多了RTT(Round Time Trip),而且還頻繁的調(diào)用系統(tǒng)IO,發(fā)送網(wǎng)絡(luò)請求。如下圖。

為了提升效率,這時候Pipeline出現(xiàn)了,它允許客戶端可以一次發(fā)送多條命令,而不等待上一條命令執(zhí)行的結(jié)果,這和網(wǎng)絡(luò)的Nagel算法有點像(TCP_NODELAY選項)。不僅減少了RTT,同時也減少了IO調(diào)用次數(shù)(IO調(diào)用涉及到用戶態(tài)到內(nèi)核態(tài)之間的切換)。如下圖:

客戶端這邊首先將執(zhí)行的命令寫入到緩沖中,最后再一次性發(fā)送Redis。但是有一種情況就是,緩沖區(qū)的大小是有限制的,比如Jedis,限制為8192,超過了,則刷緩存,發(fā)送到Redis,但是不去處理Redis的應(yīng)答,如上圖所示那樣。

1.2 實現(xiàn)原理

要支持Pipeline,其實既要服務(wù)端的支持,也要客戶端支持。對于服務(wù)端來說,所需要的是能夠處理一個客戶端通過同一個TCP連接發(fā)來的多個命令,可以理解為,這里將多個命令切分,和處理單個命令一樣(之前老生常談的黏包現(xiàn)象),Redis就是這樣處理的。而客戶端,則是要將多個命令緩存起來,緩沖區(qū)滿了就發(fā)送,然后再寫緩沖,最后才處理Redis的應(yīng)答,如Jedis。

1.3 從哪個方面提升性能

正如上面所說的,一個是RTT,節(jié)省往返時間,但是另一個原因也很重要,就是IO系統(tǒng)調(diào)用。一個read系統(tǒng)調(diào)用,需要從用戶態(tài),切換到內(nèi)核態(tài)。

1.4 注意點

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

2. Codis Pipeline

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

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

3. 總結(jié)

  1. Pipeline減少了RTT,也減少了IO調(diào)用次數(shù)(IO調(diào)用涉及到用戶態(tài)到內(nèi)核態(tài)之間的切換)

  2. 需要控制Pipeline的大小,否則會消耗Redis的內(nèi)存

  3. Codis 3.2 Pipeline默認10K,3.1則是1024
    Jedis客戶端緩存是8192,超過該大小則刷新緩存,或者直接發(fā)送

4. 參考資料

  1. Redis官方文檔:https://redis.io/topics/pipelining

http://www.cnblogs.com/jabnih/p/7157921.html