Redis是key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。
在这里对jedis关于事务、管道和分布式的调用方式做一个简单的介绍和对比:
package com.ws.Redis;
import java.util.Arrays;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;
public class RedisConnection {
public static void main(String[] args) {
normalFunction();
// transactionsFunction();
// pipeline();
// combPipelineTranscationFunction();
// shardNormalFunction();
// shardpipelinedFunction();
// shardPipelinedPoolFunction();
// shardSimplePoolFunction();
}
//简单调用方式,每次set值之后都会返回结果,标记该次set是否成功。
public static void normalFunction() {
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("123456");
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
String result = jedis.set("normal" + i, "normal" + i);
}
long end = System.currentTimeMillis();
System.out.println("Normal SET: " + ((end - start)/1000.0) + " seconds");
jedis.disconnect();
}
//redis事务,一个client发起的事务中的命令可以连续执行,中间不会插入其他指令。
//可以调用jedis.watch()方法来监控key,如果调用后的key值发生变化,则事务执行失败。使用tx.discard()方法取消事务。
//注:事务中某个操作执行失败,并不会回滚其他操作。
public static void transactionsFunction() {
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("123456");
long start = System.currentTimeMillis();
Transaction tx = jedis.multi();
for (int i = 0; i < 10000; i++) {
tx.set("transaction" + i, "transaction" + i);
}
List<Object> results = tx.exec();
long end = System.currentTimeMillis();
System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
jedis.disconnect();
}
//采用异步方式,一次发送多个set,不同步等待其返回的结果,可以极大的提升执行效率。
public static void pipeline() {
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("123456");
Pipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for(int i=0; i<10000; i++){
pipeline.set("pipeline" + i, "pipeline" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipeline SET : " +((end-start)/1000.0) + " seconds");
jedis.disconnect();
}
//在管道中使用事务,但是效率并不比但多使用事务效率高很多。
public static void combPipelineTranscationFunction() {
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("123456");
long start = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
pipeline.exec();
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
jedis.disconnect();
}
//分布式直接连接,并且是同步调用,每步执行都返回执行结果。
public static void shardNormalFunction() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380));
ShardedJedis sharding = new ShardedJedis(shards);
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = sharding.set("shardNormal" + i, "shardNormal" + i);
}
long end = System.currentTimeMillis();
System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");
sharding.disconnect();
}
//分布式直接连接,并且是异步调用,不是每步执行都返回执行结果
public static void shardpipelinedFunction() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380));
ShardedJedis sharding = new ShardedJedis(shards);
ShardedJedisPipeline pipeline = sharding.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("shardpipelined" + i, "shardpipelined" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");
sharding.disconnect();
}
//多线程方式,直接连接不安全,需要使用连接池方式,分布式连接池同步调用。
public static void shardSimplePoolFunction() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380));
ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
ShardedJedis one = pool.getResource();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = one.set("shardSimplePool" + i, "shardSimplePool" + i);
}
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");
pool.destroy();
}
//分布式连接池异步调用
public static void shardPipelinedPoolFunction() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380));
ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
ShardedJedis one = pool.getResource();
ShardedJedisPipeline pipeline = one.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("shardPipelinedPool" + i, "shardPipelinedPool" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
pool.destroy();
}
}
一些注意点:
- 事务和管道都是异步模式。在事务和管道中不能同步查询结果。比如下面两个调用,都是不允许的;
- 事务和管道都是异步的,在管道中再进行事务调用,没有必要,不如直接进行事务模式;
- 分布式中,连接池的性能比直连的性能略好;
- 分布式调用中不支持事务,因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,故没法使用事务;
测试:
Simple SET: 5.227 seconds
Transaction SET: 0.5 seconds
Pipelined SET: 0.353 seconds
Pipelined transaction: 0.509 seconds
Simple@Sharing SET: 5.289 seconds
Pipelined@Sharing SET: 0.348 seconds
Simple@Pool SET: 5.039 seconds
Pipelined@Pool SET: 0.401 seconds
另外,经测试分布式中用到的机器越多,调用会越慢。分布式中,连接池方式调用不但线程安全外,根据上面的测试数据,也可以看出连接池比直连的效率更好。