分布式快取Redis之Pipeline(管道)

2021-09-22 01:37:35 字數 3063 閱讀 3193

寫在前面

本學習教程所有示例**見github:

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的物理記憶體或網路介面的緩衝能力。

客戶端向服務端傳送乙個查詢請求,並監聽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必須在處理完所有命令前先快取起所有命令的處理結果。打包的命令越多,快取消耗記憶體也越多。所以並不是打包的命令越多越好。具體多少合適需要根據具體情況測試。

(二)比較普通模式與pipeline模式

測試環境:

windows:eclipse + jedis2.9.0 + jdk 1.7

ubuntu:部署在虛擬機器上的伺服器 redis 3.0.7

/*

* 測試普通模式與pipeline模式的效率: 

* 測試方法:向redis中插入10000組資料

*/public static void testpipelineandnormal(jedis jedis)

throws interruptedexception

long end = system.currenttimemillis();

logger.info("the jedis total time is:" + (end - start));

pipeline pipe = jedis.pipelined(); // 先建立乙個pipeline的鏈結物件

long start_pipe = system.currenttimemillis();

for (int i = 0; i < 10000; i++)

pipe.sync(); // 獲取所有的response

long end_pipe = system.currenttimemillis();

logger.info("the pipe total time is:" + (end_pipe - start_pipe));

blockingqueuelogqueue = new linkedblockingqueue();

long begin = system.currenttimemillis();

for (int i = 0; i < 10000; i++)

long stop = system.currenttimemillis();

logger.info("the blockingqueue total time is:" + (stop - begin));

}

從上述**以及結果中可以明顯的看到pipeline在「批量處理」時的優勢。

(三)適用場景

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

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

(四)管道(pipelining) vs 指令碼(scripting)

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

Redis 分布式快取

1 官網 3 菜鳥教程 4 redis的集群教程 5 史上最全redis高可用技術解決方案大全 一 redis的特點?redis 本質上是乙個 key value 型別的記憶體資料庫,很像 memcached,整個 資料庫統統載入在記憶體當中進行操作,定期通過非同步操作把資料庫資料 flush 到硬...

分布式快取Redis之bitmap setbit

本學習教程所有示例 見github 1 setbit redis 127.0.0.1 6379 setbit key name offset value 該命令用於對 key 所儲存的字串值,設定或清除指定偏移量上的位 bit 時間複雜度o 1 在redis中,儲存的字串都是以二進位制的形式存在的。...

分布式快取Redis之資源釋放

本學習教程所有示例 見github 比如下面一段 for int i 2000 i 3000 i 在上面的 中,當迴圈到600次的時候,就會報出拿不到連線的錯誤,而600恰好是配置中的連線個數 並且jedis連線不會在使用結束後進行自動釋放 因為報錯後,再次請求這個方法,連乙個連線都拿不到了 如果在...