Redis Pipeline講解

一、pipeline出現的背景

Redis是一種基於客戶端-服務端(CS)模型以及請求/響應協議的TCP服務,這意味着通常情況下一個請求會遵循以下步驟:

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

這樣一次命令消耗的時間就包括四個部分:請求從客戶端到服務器的時間命令排隊的時間命令真正執行時間、結果從服務器到客戶端的時間,從第一個到第四個消耗的時間總和稱爲 Round Trip Time(簡稱RTT,往返時間)。當客戶端與服務器存在網絡延遲時,RTT就可能會很大,這樣就會導致性能問題。

 

二、作用與原理

管道(Pipeline)就是爲了改善這個情況的,利用管道技術,客戶端可以一次性發送多個請求而不用等待服務器的響應,待所有命令都發送完後再一次性讀取服務的響應,這樣可以極大的降低RTT時間從而提升性能。另一個原因也很重要,就是減少IO系統調用。例如,一個read或write系統調用,需要從用戶態,切換到內核態。

要支持 pipeline,既要服務端的支持,也要客戶端支持。對於服務端來說,所需要的是能夠處理一個客戶端通過同一個 TCP 連接發來的多個命令。對於客戶端,則是要將多個命令緩存起來,緩衝區滿了就發送,然後再寫緩衝區,最後才處理 Redis 的應答。

Redis的管道在客戶端通常會設置一個命令緩衝區來存儲即將被批量發送的命令,客戶端首先將執行的命令寫入到緩衝區中,最後再一次性發送 Redis。但是有一種情況就是,緩衝區的大小是有限制的,如果命令數據太大,可能會有多次發送的過程,但是仍不會處理 Redis 的應答。比如Jedis,限制爲8192,超過了,則刷緩存,發送到Redis,但是不去處理Redis的應答。

如果客戶端使用管道發送了多條命令,那麼服務器就會將多條命令放入一個隊列中,這一操作會消耗一定的內存。而且redis必須在處理完所有命令前先緩存起所有命令的處理結果,這也是耗內存的,所以管道中命令的數量並不是越大越好(太大容易撐爆內存),而是應該有一個合理的值。

 

三、舉例說明

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

其執行過程如下圖所示:

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

而管道(pipeline)可以一次性發送多條命令並在執行完後一次性將結果返回,pipeline通過減少客戶端與redis的通信次數來實現降低RTT,而且Pipeline 實現的原理是隊列,而隊列是先進先出的,這樣就保證數據的順序性。其過程如下圖所示:client可以將三個命令放到一個tcp報文一起發送,server則可以將三條命令的處理結果放到一個tcp報文返回。

 

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

  • 原生批量命令是原子性Pipeline是非原子性的。
  • 原生批量命令是一個命令對應多個keyPipeline支持多個命令。
  • 原生批量命令是Redis服務端支持實現的,而Pipeline需要服務端與客戶端的共同實現。

 

四、Pipeline正確使用方式

Pipeline雖然好用,但是每次Pipeline組裝的命令個數不能沒有節制,否則一次組裝Pipeline數據量過大,一方面會增加客戶端的等待時機,另一方面會造成一定的網絡阻塞,可以將一次包含大量命令的Pipeline拆分成多次較小的Pipeline來完成。而且Pipeline每次只能作用在一個Redis節點。

 

參考:

https://blog.csdn.net/qq_24313635/article/details/83054300

https://blog.csdn.net/w1lgy/article/details/84455579

https://blog.csdn.net/u011489043/article/details/78769428

https://segmentfault.com/a/1190000011440752

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