redis是一個cs模式的tcp server,使用和http類似的請求響應協議。一個client可以通過一個socket連接發起多個請求命令。每個請求命令發出后client通常會阻塞并等待redis服務處理,redis處理完后請求命令后會將結果通過響應報文返回給client。基本的通信過程如下:
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的測試:
package com.jd.redis.client;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline;
publicclass PipelineTest {
/** * @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。
文章列表