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...