Jedis
Jedis 是 Redis 官方推荐的 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)。这可能是官方客户端访问方式有所不同(毕竟是模拟的多客户端),后面有机会再研究一下这方面的源码。
Comments
comments powered by Disqus