簡體   English   中英

為什么在redis中使用管道時,有100,000條記錄這么慢?

[英]why it is so slow with 100,000 records when using pipeline in redis?

據說在redis中需要很多set/getpipeline是更好的方法,所以這是我的測試代碼:

public class TestPipeline {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
        List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
        list.add(si);
        ShardedJedis jedis = new ShardedJedis(list);
        long startTime = System.currentTimeMillis();
        ShardedJedisPipeline pipeline = jedis.pipelined();
        for (int i = 0; i < 100000; i++) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("id", "" + i);
            map.put("name", "lyj" + i);
            pipeline.hmset("m" + i, map);
        }
        pipeline.sync();
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }
}

當我運行它時,這個程序暫時沒有響應,但是當我不使用pipe ,它只需要20073毫秒,所以我很困惑,為什么沒有pipeline甚至更好的差距!

謝謝你回答我,幾個問題,你如何計算6MB數據? 當我發送10K數據時,管道總是比正常模式更快,但是100k,管道沒有響應。我認為100-1000操作是一個明智的選擇,如下所述。是不是有任何JIT,因為我不明白?

在編寫此類基准測試之前需要考慮幾點(尤其是使用JVM的基准測試):

  • 在大多數(物理)機器上,當使用流水線時,Redis能夠處理超過100K的操作數/秒。 您的基准僅處理100K項目,因此它的持續時間不足以產生有意義的結果。 此外,JIT的連續階段沒有時間開始。

  • 絕對時間不是一個非常相關的指標。 在保持基准運行至少10秒的同時顯示吞吐量(即每秒的操作次數)將是更好且更穩定的度量。

  • 你的內循環會產生大量垃圾。 如果您計划對Jedis + Redis進行基准測試,那么您需要將自己程序的開銷保持在較低水平。

  • 因為你已經在main函數中定義了所有內容,所以你的循環不會被JIT編譯(取決於你使用的JVM)。 只有內部方法調用可能是。 如果希望JIT高效,請確保將代碼封裝到可由JIT編譯的方法中。

  • 可選地,您可能希望在執行實際測量之前添加預熱階段,以避免計算使用裸骨解釋器運行第一次迭代的開銷以及JIT本身的成本。

現在,關於Redis流水線,您的管道太長了。 管道中的100K命令意味着Jedis必須在向Redis發送任何內容之前構建一個6MB的緩沖區。 這意味着套接字緩沖區(在客戶端,可能是服務器端)將飽和,並且Redis也必須處理6 MB通信緩沖區。

此外,您的基准測試仍然是同步的(使用管道不會神奇地使其異步)。 換句話說,在將管道的最后一個查詢發送到Redis之前,Jedis不會開始閱讀回復。 當管道太長時,它有可能阻塞事物。

考慮將管道的大小限制為100-1000個操作。 當然,它會產生更多的往返,但通信棧的壓力將降低到可接受的水平。 例如,考慮以下程序:

import redis.clients.jedis.*;
import java.util.*;

public class TestPipeline {

    /**
     * @param args
     */

    int i = 0; 
    Map<String, String> map = new HashMap<String, String>();
    ShardedJedis jedis;  

    // Number of iterations
    // Use 1000 to test with the pipeline, 100 otherwise
    static final int N = 1000;

    public TestPipeline() {
      JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
      List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
      list.add(si);
      jedis = new ShardedJedis(list);
    } 

    public void push( int n ) {
     ShardedJedisPipeline pipeline = jedis.pipelined();
     for ( int k = 0; k < n; k++) {
      map.put("id", "" + i);
      map.put("name", "lyj" + i);
      pipeline.hmset("m" + i, map);
      ++i;
     }
     pipeline.sync(); 
    }

    public void push2( int n ) {
     for ( int k = 0; k < n; k++) {
      map.put("id", "" + i);
      map.put("name", "lyj" + i);
      jedis.hmset("m" + i, map);
      ++i;
     }
    }

    public static void main(String[] args) {
      TestPipeline obj = new TestPipeline();
      long startTime = System.currentTimeMillis();
      for ( int j=0; j<N; j++ ) {
       // Use push2 instead to test without pipeline
       obj.push(1000); 
       // Uncomment to see the acceleration
       //System.out.println(obj.i);
     }
     long endTime = System.currentTimeMillis();
     double d = 1000.0 * obj.i;
     d /= (double)(endTime - startTime);
     System.out.println("Throughput: "+d);
   }
 }

使用此程序,您可以使用或不使用流水線進行測試。 使用流水線時,請務必增加迭代次數(N參數),以使其運行至少10秒。 如果你在循環中取消注釋println,你會發現程序在開始時很慢並且隨着JIT開始優化而變得更快(這就是為什么程序應該運行至少幾秒鍾以產生有意義的結果)。

在我的硬件(舊的Athlon盒子)上,當使用管道時,我可以獲得8-9倍的吞吐量。 通過優化內循環中的鍵/值格式化並添加預熱階段,可以進一步改進程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM