Jedis

JedisRedis 官方推荐的 Java 客户端,目前已经更新到 2.7 版本,支持 Redis 2.8.x ~ 3.0.x。

Jedis 的 Maven 依赖包如下:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.7.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

单条数据处理模式

此模式下 Jedis 的每一个操作都会被当作一个独立的 “Request-Response” 模型进行执行,这种模式的使用非常简单:

1
2
3
4
5
6
7
JedisPool pool = new JedisPool("redis.host");
Jedis jedis = pool.getResource();
jedis.set("poop", "poobar");
String poobar = jedis.get("poop");

jedis.close();
pool.close();

批量数据处理模式

批量数据处理模式主要是指通过 Pipeline(管道) 缓冲数据,将多个 command,依次发送给 server 的一种批处理模式(注意,这里的批处理与事务是不同的概念,批处理并不能保证请求的成功性)。在 server 处理 command 期间,客户端无法获得单个 command 的响应数据,只有在 server 处理完成、关闭 Pipeline 请求之后才可以依次获取每个 command 的响应结果,也就是说每次获取结果之前关闭 Pipeline 请求的过程是必不可少的。

1
2
3
4
5
6
7
8
9
JedisPool pool = new JedisPool("redis.host");
Jedis jedis = pool.getResource();
Pipeline pipeline = jedis.pipelined();
Response<String> response = pipeline.get("poop");
pipeline.sync();
String poobar = response.get();

jedis.close();
pool.close();

由于 Pipeline 实际上减少了客户端与服务端之间反复的连接时间,使得数据处理的效率大大提高。笔者在一开始使用 Jedis 时发现效率极低,基本上一秒钟只能处理几千条数据,而且不论怎么优化 Redis,这个结果都没有明显的提高。为此笔者纠结了很长一段时间不得其解,直到最近了解到 Pipeline 这个利器之后才豁然开朗。下面的 Benchmark 测试结果就直接显示了两种方式的性能差异。


Benchmark

基本信息

  • 单点 Redis 服务器(虚拟机环境)
  • CPU:Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz,核心数:2
  • 内存:3.8 G
  • 操作系统:CentOS release 5.4
  • 不开启 DB save
  • 单线程客户端
  • 由于几次试验测试结果相似,就选取一次的测试结果作为最终结果,没有使用多次测试选取均值的方式,可能与真实性能稍微有些出入
  • 单条数据处理指的是直接使用 Jedis 接口操作,批量处理指的是使用 Pipeline 接口操作
  • 网络访问指的是客户端与 Redis 服务器位于同一网段的不同机器上,本机访问指的是客户端与 Redis 服务器位于同一台机器上
  • 简单起见,读写的 key 设置为字符串 "Jedis:i"(i 为循环的次数),value 与 key 相同

网络访问

单条数据处理

  • 百万级数据写(key-String, value-String)

结果 452,730,121,946 纳秒 约等于 453 秒

  • 百万级数据读

结果 508,386,552,562 纳秒 约等于 508 秒

  • 百万级数据删除

结果 508,714,438,156 纳秒 约等于 509 秒

批量数据处理

  • 百万级数据写(key-String, value-String)

结果 6,151,898,431 纳秒 约等于 6 秒

  • 百万级数据读

结果 3,519,384,281 纳秒 约等于 3 秒

  • 百万级数据删除

结果 5,186,055,510 纳秒 约等于 5 秒

本机访问

单条数据处理

说明:为了减少测试时间,这里只测试了1W条数据的读写

  • 万级数据写(key-String, value-String)

结果 1,199,071,735 纳秒 约等于 1 秒

对比:网络访问单条数据处理模式万级数据写时间约等于 5 秒

  • 万级数据读

结果 1,111,957,283 纳秒 约等于 1 秒

  • 万级数据删除

结果 1,121,649,753 纳秒 约等于 1 秒

批量数据处理

  • 万级数据写(key-String, value-String)

结果 352,212,255 纳秒 约等于 0.35 秒

  • 万级数据读

结果 344,821,892 纳秒 约等于 0.34 秒

  • 万级数据删除

结果 341,540,726 纳秒 约等于 0.7 秒

  • 十万级数据写(key-String, value-String)

结果 827,447,969 纳秒 约等于 0.8 秒

  • 十万级数据读

结果 718,332,980 纳秒 约等于 0.7 秒

  • 十万级数据删除

结果 758,594,043 纳秒 约等于 0.7 秒

  • 百万级数据写(key-String, value-String)

结果 6,105,554,458 纳秒 约等于 6 秒

  • 百万级数据读

结果 4,034,674,889 纳秒 约等于 4 秒

  • 百万级数据删除

结果 4,921,311,797 纳秒 约等于 5 秒

上面的结果可以看出 Pipeline 的读写方式比普通的读写方式提高了近百倍的效率,差距还是相当明显的。

多线程

在一个客户端上开启 100 个线程写 10000 条数据,吞吐率约为 190,222 res/s。

在继续增加线程到 1000 个时报错:

Exception in thread "main" java.util.concurrent.ExecutionException: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at com.enjoyor.jedis.benchmark.Benchmark.main(Benchmark.java:97)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at redis.clients.util.Pool.getResource(Pool.java:50)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:86)
    at com.enjoyor.jedis.benchmark.Benchmark$Ctest.call(Benchmark.java:57)
    at com.enjoyor.jedis.benchmark.Benchmark$Ctest.call(Benchmark.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

这是 Redis 的连接数限制的原因,需要修改 maxclients 来增大连接数。


Redis-benchmark

Redis 自带了一个 benchmark 工具,这里就使用这个官方的 redis-benchmark 工具进行对比验证。

万级数据

[root@hd124]$>src/redis-benchmark -n 10000
====== PING_INLINE ======
  10000 requests completed in 0.13 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.01% <= 1 milliseconds
99.51% <= 5 milliseconds
99.79% <= 6 milliseconds
100.00% <= 6 milliseconds
75187.97 requests per second

====== PING_BULK ======
  10000 requests completed in 0.13 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

100.00% <= 0 milliseconds
79365.08 requests per second

====== SET ======
  10000 requests completed in 0.13 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.93% <= 1 milliseconds
100.00% <= 1 milliseconds
78125.00 requests per second

====== GET ======
  10000 requests completed in 0.13 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.58% <= 1 milliseconds
100.00% <= 1 milliseconds
78740.16 requests per second

百万级数据

[root@hd124]$>src/redis-benchmark -n 1000000
====== PING_INLINE ======
  1000000 requests completed in 11.54 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.95% <= 1 milliseconds
99.98% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 4 milliseconds
100.00% <= 5 milliseconds
100.00% <= 5 milliseconds
86662.62 requests per second

====== PING_BULK ======
  1000000 requests completed in 11.47 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.98% <= 1 milliseconds
99.99% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 4 milliseconds
100.00% <= 4 milliseconds
87214.38 requests per second

====== SET ======
  1000000 requests completed in 11.70 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.67% <= 1 milliseconds
99.95% <= 2 milliseconds
99.97% <= 3 milliseconds
99.98% <= 4 milliseconds
99.99% <= 5 milliseconds
99.99% <= 6 milliseconds
100.00% <= 8 milliseconds
100.00% <= 8 milliseconds
85448.17 requests per second

====== GET ======
  1000000 requests completed in 11.48 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.97% <= 1 milliseconds
99.99% <= 2 milliseconds
100.00% <= 4 milliseconds
100.00% <= 4 milliseconds
87077.67 requests per second

官方的结果显示请求数在万级数据量的大小时效率比 Jedis 要稍微高一点(0.35s > 0.13s),但是当数据量增加到百万级的时候效率就要低很多了(6s < 11.7s)。这可能是官方客户端访问方式有所不同(毕竟是模拟的多客户端),后面有机会再研究一下这方面的源码。


Reference

  1. https://github.com/xetorthio/jedis
  2. http://shift-alt-ctrl.iteye.com/blog/1863790



Comments

comments powered by Disqus