Pipeline詳解

這瓜保熟麼發表於2021-01-01

一、pipeline出現的背景:

redis客戶端執行一條命令分4個過程:

傳送命令-〉命令排隊-〉命令執行-〉返回結果

這個過程稱為Round trip time(簡稱RTT, 往返時間),mget mset有效節約了RTT,但大部分命令(如hgetall,並沒有mhgetall)不支援批量操作,需要消耗N次RTT ,這個時候需要pipeline來解決這個問題

二、pepeline的效能

1、未使用pipeline執行N條命令

在這裡插入圖片描述


2、使用了pipeline執行N條命令 

 

在這裡插入圖片描述

3、兩者效能對比

在這裡插入圖片描述

三、原生批命令(mset, mget)與Pipeline對比

1、原生批命令是原子性,pipeline是非原子性

(原子性概念:一個事務是一個不可分割的最小工作單位,要麼都成功要麼都失敗。原子操作是指你的一個業務邏輯必須是不可拆分的. 處理一件事情要麼都成功,要麼都失敗,原子不可拆分)

2、原生批命令一命令多個key, 但pipeline支援多命令(存在事務),非原子性

3、原生批命令是服務端實現,而pipeline需要服務端與客戶端共同完成

四、Pipeline正確使用方式

使用pipeline組裝的命令個數不能太多,不然資料量過大,增加客戶端的等待時間,還可能造成網路阻塞,可以將大量命令的拆分多個小的pipeline命令完成。

五、簡介

  Redis 的 pipeline(管道)功能在命令列中沒有,但 redis 是支援 pipeline 的,而且在各個語言版的 client 中都有相應的實現。 由於網路開銷延遲,就算 redis server 端有很強的處理能力,也會由於收到的 client 訊息少,而造成吞吐量小。當 client 使用 pipelining 傳送命令時,redis server 必須將部分請求放到佇列中(使用記憶體),執行完畢後一次性傳送結果;如果傳送的命令很多的話,建議對返回的結果加標籤,當然這也會增加使用的記憶體;

  Pipeline 在某些場景下非常有用,比如有多個 command 需要被“及時的”提交,而且他們對相應結果沒有互相依賴,對結果響應也無需立即獲得,那麼 pipeline 就可以充當這種“批處理”的工具;而且在一定程度上,可以較大的提升效能,效能提升的原因主要是 TCP 連線中減少了“互動往返”的時間

  不過在編碼時請注意,pipeline 期間將“獨佔”連結,此期間將不能進行非“管道”型別的其他操作,直到 pipeline 關閉;如果你的 pipeline 的指令集很龐大,為了不干擾連結中的其他操作,你可以為 pipeline 操作新建 Client 連結,讓 pipeline 和其他正常操作分離在2個 client 中。不過 pipeline 事實上所能容忍的操作個數,和 socket-output 緩衝區大小/返回結果的資料尺寸都有很大的關係;同時也意味著每個 redis-server 同時所能支撐的 pipeline 連結的個數,也是有限的,這將受限於 server 的實體記憶體或網路介面的緩衝能力。

Redis 使用的是客戶端-伺服器(CS)模型請求/響應協議的 TCP 伺服器。這意味著通常情況下一個請求會遵循以下步驟:

  • 客戶端向服務端傳送一個查詢請求,並監聽 Socket 返回,通常是以阻塞模式,等待服務端響應。
  • 服務端處理命令,並將結果返回給客戶端。

  Redis 客戶端與 Redis 伺服器之間使用 TCP 協議進行連線,一個客戶端可以通過一個 socket 連線發起多個請求命令。每個請求命令發出後 client 通常會阻塞並等待 redis 伺服器處理,redis 處理完請求命令後會將結果通過響應報文返回給 client,因此當執行多條命令的時候都需要等待上一條命令執行完畢才能執行。比如:

  這裡寫圖片描述

  其執行過程如下圖所示:

這裡寫圖片描述

由於通訊會有網路延遲,假如 client 和 server 之間的包傳輸時間需要0.125秒。那麼上面的三個命令6個報文至少需要0.75秒才能完成。這樣即使 redis 每秒能處理100個命令,而我們的 client 也只能一秒鐘發出四個命令。這顯然沒有充分利用 redis 的處理能力。

  而管道(pipeline)可以一次性傳送多條命令並在執行完後一次性將結果返回,pipeline 通過減少客戶端與 redis 的通訊次數來實現降低往返延時時間,而且 Pipeline 實現的原理是佇列,而佇列的原理是時先進先出,這樣就保證資料的順序性。 Pipeline 的預設的同步的個數為53個,也就是說 arges 中累加到53條資料時會把資料提交。其過程如下圖所示:client 可以將三個命令放到一個 tcp 報文一起傳送,server 則可以將三條命令的處理結果放到一個 tcp 報文返回。

這裡寫圖片描述

需要注意到是用 pipeline 方式打包命令傳送,redis 必須在處理完所有命令前先快取起所有命令的處理結果。打包的命令越多,快取消耗記憶體也越多。所以並不是打包的命令越多越好。具體多少合適需要根據具體情況測試。

 

六、適用場景

  有些系統可能對可靠性要求很高,每次操作都需要立馬知道這次操作是否成功,是否資料已經寫進 redis 了,那這種場景就不適合。

  還有的系統,可能是批量的將資料寫入 redis,允許一定比例的寫入失敗,那麼這種場景就可以使用了,比如10000條一下進入 redis,可能失敗了2條無所謂,後期有補償機制就行了,比如簡訊群發這種場景,如果一下群發10000條,按照第一種模式去實現,那這個請求過來,要很久才能給客戶端響應,這個延遲就太長了,如果客戶端請求設定了超時時間5秒,那肯定就丟擲異常了,而且本身群發簡訊要求實時性也沒那麼高,這時候用 pipeline 最好了。

七、管道(Pipelining) VS 指令碼(Scripting)

  大量 pipeline 應用場景可通過 Redis 指令碼(Redis 版本 >= 2.6)得到更高效的處理,後者在伺服器端執行大量工作。指令碼的一大優勢是可通過最小的延遲讀寫資料,讓讀、計算、寫等操作變得非常快(pipeline 在這種情況下不能使用,因為客戶端在寫命令前需要讀命令返回的結果)。

  應用程式有時可能在 pipeline 中傳送 EVAL 或 EVALSHA 命令。Redis 通過 SCRIPT LOAD 命令(保證 EVALSHA 成功被呼叫)明確支援這種情況。