速度不夠,管道來湊 Redis管道技術

2021-09-20 10:39:01 字數 3167 閱讀 5275

redis客戶端與伺服器之間使用tcp協議進行通訊,並且很早就支援管道(pipelining)技術了。在某些高併發的場景下,網路開銷成了redis速度的瓶頸,所以需要使用管道技術來實現突破。

在介紹管道之前,先來想一下單條命令的執行步驟:

按照這樣的描述,每個命令的執行時間 = 客戶端傳送時間+伺服器處理和返回時間+乙個網路來回的時間

其中乙個網路來回的時間是不固定的,它的決定因素有很多,比如客戶端到伺服器要經過多少跳,網路是否擁堵等等。但是這個時間的量級也是最大的,也就是說乙個命令的完成時間的長度很大程度上取決於網路開銷。如果我們的伺服器每秒可以處理10萬條請求,而網路開銷是250毫秒,那麼實際上每秒鐘只能處理4個請求。最暴力的優化方法就是使客戶端和伺服器在一台物理機上,這樣就可以將網路開銷降低到1ms以下。但是實際的生產環境我們並不會這樣做。而且即使使用這種方法,當請求非常頻繁時,這個時間和伺服器處理時間比較仍然是很長的。

redis pipelining

在redis中,如果客戶端使用管道傳送了多條命令,那麼伺服器就會將多條命令放入乙個佇列中,這一操作會消耗一定的記憶體,所以管道中命令的數量並不是越大越好(太大容易撐爆記憶體),而是應該有乙個合理的值。

深入理解redis互動流程

管道並不只是用來網路開銷延遲的一種方法,它實際上是會提公升redis伺服器每秒操作總數的。在解釋原因之前,需要更深入的了解redis命令處理過程。

乙個完整的互動流程如下:

客戶端程序呼叫write()把訊息寫入到作業系統核心為socket分配的send buffer中

作業系統會把send buffer中的內容寫入網絡卡,網絡卡再通過閘道器路由把內容傳送到伺服器端的網絡卡

服務端網絡卡會把接收到的訊息寫入作業系統為socket分配的recv buffer

伺服器程序呼叫read()讀取訊息然後進行處理

處理完成後呼叫write()把返回結果寫入到伺服器端的send buffer

伺服器作業系統再將send buffer中的內容寫入網絡卡,然後傳送到客戶端

客戶端作業系統將網絡卡內容讀到recv buffer中

客戶端程序呼叫read()從recv buffer中讀取訊息並返回

現在我們把命令執行的時間進一步細分:

命令的執行時間 = 客戶端呼叫write並寫網絡卡時間+一次網路開銷的時間+服務讀網絡卡並呼叫read時間++伺服器處理資料時間+服務端呼叫write並寫網絡卡時間+客戶端讀網絡卡並呼叫read時間

這其中除了網路開銷,花費時間最長的就是進行系統呼叫write()read()了,這一過程需要作業系統由使用者態切換到核心態,中間涉及到的上下文切換會浪費很多時間。

使用管道時,多個命令只會進行一次read()wrtie()系統呼叫,因此使用管道會提公升redis伺服器處理命令的速度,隨著管道中命令的增多,伺服器每秒處理請求的數量會線性增長,最後會趨近於不使用管道的10倍。

和scripting對比

對於管道的大部分應用場景而言,使用redis指令碼(redis2.6及以後的版本)會使伺服器端有更好的表現。使用指令碼最大的好處就是可以以最小的延遲讀寫資料。

有時我們也需要在管道中使用eval和evalsha命令,這是完全有可能的。因此redis提供了script load命令來支援這種情況。

眼見為實

多說無益,還是眼見為實。下面就來對比一下使用管道和不使用管道的速度差異。

public

class

jedisdemo

private

static

void

withoutpipeline

(jedis jedis)

long end = system.

currenttimemillis()

;long cost = end - start;

system.out.

println

("withoutpipeline cost : "

+ cost +

" ms");

}private

static

void

withpipeline

(jedis jedis)

pipe.

sync()

;// 獲取所有的response

long end_pipe = system.

currenttimemillis()

;long cost_pipe = end_pipe - start_pipe;

system.out.

println

("withpipeline cost : "

+ cost_pipe +

" ms");

}}

結果也符合我們的預期:

withoutpipeline cost : 11791 ms

withpipeline cost : 55 ms

總結

使用管道技術可以顯著提公升redis處理命令的速度,其原理就是將多條命令打包,只需要一次網路開銷,在伺服器端和客戶端各一次read()write()系統呼叫,以此來節約時間。

管道中的命令數量要適當,並不是越多越好。

redis2.6版本以後,指令碼在大部分場景中的表現要優於管道。 擴充套件

前面我們提到,為了解決網路開銷帶來的延遲問題,可以把客戶端和伺服器放到一台物理機上。但是有時用benchmark進行壓測的時候發現這仍然很慢。

這時客戶端和服務端實際是在一台物理機上的,所有的操作都在記憶體中進行,沒有網路延遲,按理來說這樣的操作應該是非常快的。為什麼會出現上面的情況的呢?

實際上,這是由核心排程導致的。比如說,benchmark執行時,讀取了伺服器返回的結果,然後寫了乙個新的命令。這個命令就在回環介面的send buffer中了,如果要執行這個命令,核心需要喚醒redis伺服器程序。所以在某些情況下,本地介面也會出現類似於網路延遲的延遲。其實是核心特別繁忙,一直沒有排程到redis伺服器程序。

參考redis官方文件

redis原始碼

掘金小冊:《redis 深度歷險:核心原理與應用實踐》

Redis 管道加快Redis 執行速度說明

我在做 timeline 的demo 例項 的訊息推送的時候發現推送的速度怎麼如此之慢大概每秒只處理4千條佇列 如下 後端推送timeline 的demo 例項 include once redisdb.class.php rdb new redisdb 192.168.1.252 6379 whi...

redis管道例子

管道好處 假 設不會因為tcp 報文過長而被拆分。可能兩個tcp報文就能完成四條命令,client可以將四個incr命令放到乙個tcp報文一起傳送,server則可以將四條命令 的處理結果放到乙個tcp報文返回。通過pipeline方式當有大批量的操作時候。我們可以節省很多原來浪費在網路延遲的時間。...

Redis管道傳輸

redis是乙個tcp 伺服器,並支援請求 響應協議。redis的乙個請求完成需要下面的步驟 管道的基本含義是,客戶端可以傳送多個請求給伺服器,而無需等待答覆所有,並最後讀取在單個步驟中的答應。要檢查redis的管道,只要開始redis的例項,然後在終端鍵入以下命令。echo en ping r n...