redis是一個cs模式的tcp server,使用和http相似的請求響應協議。一個client能夠經過一個socket鏈接發起多個請求命令。每一個請求命令發出後client一般會阻塞並等待redis服務處理,redis處理完後請求命令後會將結果經過響應報文返回給client。基本的通訊過程以下:redis
Client: INCR X |
基本上四個命令須要8個tcp報文才能完成。因爲通訊會有網絡延遲,假如從client和server之間的包傳輸時間須要0.125秒。那麼上面的四個命令8個報文至少會須要1秒才能完成。這樣即便redis每秒能處理100個命令,而咱們的client也只能一秒鐘發出四個命令。這顯示沒有充分利用 redis的處理能力。除了能夠利用mget,mset 之類的單條命令處理多個key的命令外咱們還能夠利用pipeline的方式從client打包多條命令一塊兒發出,不須要等待單條命令的響應返回,而redis服務端會處理完多條命令後會將多條命令的處理結果打包到一塊兒返回給客戶端。通訊過程以下:網絡
Client: INCR X |
假設不會由於tcp報文過長而被拆分。可能兩個tcp報文就能完成四條命令,client能夠將四個incr命令放到一個tcp報文一塊兒發送,server則能夠將四條命令的處理結果放到一個tcp報文返回。經過pipeline方式當有大批量的操做時候。咱們能夠節省不少原來浪費在網絡延遲的時間。須要注意到是用 pipeline方式打包命令發送,redis必須在處理完全部命令前先緩存起全部命令的處理結果。打包的命令越多,緩存消耗內存也越多。因此並是否是打包的命令越多越好。具體多少合適須要根據具體狀況測試。下面是個jedis客戶端使用pipeline的測試:tcp
package com.jd.redis.client;學習
import redis.clients.jedis.Jedis;測試 import redis.clients.jedis.Pipeline;spa
publicclass PipelineTest {.net
/**server * @param args */ publicstaticvoid main(String[] args) {
int count = 1000;
long start = System.currentTimeMillis(); withoutPipeline(count); long end = System.currentTimeMillis(); System.out.println("withoutPipeline: " + (end-start));
start = System.currentTimeMillis(); usePipeline(count); end = System.currentTimeMillis(); System.out.println("usePipeline: " + (end-start));
}
privatestaticvoid withoutPipeline(int count){ Jedis jr = null; try { jr = new Jedis("10.10.224.44", 6379); for(int i =0; i<count; i++){ jr.incr("testKey1"); } } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null){ jr.disconnect(); } } }
privatestaticvoid usePipeline(int count){ Jedis jr = null; try { jr = new Jedis("10.10.224.44", 6379); Pipeline pl = jr.pipelined(); for(int i =0; i<count; i++){ pl.incr("testKey2"); } pl.sync(); } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null){ jr.disconnect(); } } } } |
輸出:
withoutPipeline: 11341 usePipeline: 344 |
測試結果仍是很明顯有較大的差距,因此屢次操做用pipeline仍是有明顯的優點。我用的是Win7中的Jedis Java客戶端程序鏈接局域網的Linux虛擬機上的Redis Server。