当前位置: 首页 > 面试题库 >

在redis中使用管道时,为什么有100,000条记录这么慢?

沈子实
2023-03-14
问题内容

据说pipelineset/getredis需要很多时,这是一种更好的方法,所以这是我的测试代码:

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 ops / s。您的基准测试仅处理10万个项目,因此持续时间不足以产生有意义的结果。此外,JIT的后续阶段没有时间开始。

  • 绝对时间不是一个非常相关的指标。在保持基准至少运行10秒的同时显示吞吐量(即每秒的操作数)将是一个更好,更稳定的指标。

  • 您的内部循环会产生大量垃圾。如果您打算对Jedis + Redis进行基准测试,则需要将自己程序的开销保持在较低水平。

  • 因为您已将所有内容定义到main函数中,所以JIT不会编译您的循环(取决于您使用的JVM)。只能是内部方法调用。如果希望JIT高效,请确保将代码封装到JIT可以编译的方法中。

  • (可选)您可能希望在执行实际测量之前添加一个预热阶段,以避免考虑使用准系统解释器运行第一次迭代的开销以及JIT本身的成本。

现在,关于Redis管道,您的管道太长了。流水线中的100K命令意味着Jedis必须先构建6MB缓冲区,然后再向Redis发送任何内容。这意味着套接字缓冲区(在客户端,甚至在服务器端)将饱和,并且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倍。通过优化内部循环中的键/值格式并添加预热阶段,可以进一步改善该程序。



 类似资料:
  • 我注意到,当我使用条件断点进行调试时,执行速度会大大减慢。我知道这一点已经有一段时间了,现在想明白为什么。到底是什么原因导致执行如此缓慢?我知道正在添加一个条件,但是如果我自己添加条件,我不会减慢执行速度。 例如,假设我们有以下代码。假设我们添加了一个条件断点。让我们将条件设置为i==10000。 现在让我们自己写条件。 90秒完成击球(包括开始的9秒) 日食: ~9秒到达断点 第二个示例几乎是在

  • 我正在构建一个非常简单的应用程序,这样我就可以理解slf4j、桥接库、日志实现等。我的应用程序依赖于slf4j,它使用日志实现。此外,它还引入了一个包含公共日志库的库。 应用程序依赖关系: 库依赖项: 当我没有包含时,库将按照预期通过JCL输出日志记录。当我包含时,通过JCL记录器的任何日志记录都将被slf4j拾取并重定向到Logback。 我不断在internet上读到一些文章,这些文章说,如果

  • 问题内容: 我正在尝试这样的事情 输出.py 输入.py 在cmd行 但它返回 EOFError 。有人可以告诉我我在做什么错吗? 谢谢你的帮助。 编辑 Patrick Harrington解决方案有效,但我不知道为什么… 更改为: 输出将是: 给我问候 问候是:你好 问题答案: 我在Windows机器上对此进行了测试,如果您指定Python exe,它将可以正常工作: 但是如果直接以以下方式运行

  • 本文向大家介绍单线程的 Redis 为什么这么快?相关面试题,主要包含被问及单线程的 Redis 为什么这么快?时的应答技巧和注意事项,需要的朋友参考一下 Redis 有多快?官方给出的答案是读写速度 10万/秒,如果说这是在单线程情况下跑出来的成绩,你会不会惊讶?为什么单线程的 Redis 速度这么快?原因有以下几点: 纯内存操作:Redis 是完全基于内存的,所以读写效率非常的高,当然 Red

  • 我是yaml的新手,我有一个关于用于多行的管道符号()的问题。YAML有类似下面的语法吗? 测试:6+ parser$ParserException:第17行,第12列:需要“block end”,但发现:block mapping start“。

  • 我是一个Flex/Actionscript难民,试图用JS/HTML5/CSS3实现我的目标。有些事情可以立竿见影,但还有一些事情却没有立竿见影。 我正在看这个JSFIDLE“纯Javascript可拖动”,我不理解这一行(或者更确切地说,我理解它在做什么,但不理解它是如何做的) 我查了一下“document.all”,它似乎是元素的缩写。queryselectoral()没有参数? 返回从调用它