一、普通同步方式
public void test1Normal() { Jedis jedis = new Jedis("localhost"); //生成Jedis,连接到redis服务器 long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = jedis.set("n" + i, "n" + i);//写入一条记录 } long end = System.currentTimeMillis(); System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); //断开连接 }
二、事务方式(Transactions)
redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。
(事务中某个操作失败,并不会回滚其他操作。这一点需要注意。)
public void test2Trans() { Jedis jedis = new Jedis("localhost"); long start = System.currentTimeMillis(); Transaction tx = jedis.multi(); //开启事务 for (int i = 0; i < 100000; i++) { tx.set("t" + i, "t" + i); } List<Object> results = tx.exec(); //提交事务 long end = System.currentTimeMillis(); System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
1.WATCH命令可以监控一个或多个键,一旦其中有一个键里的值被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令
(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值)
2.我们调用jedis.watch(…)方法来监控key,如果调用后key里的值发生变化,则整个事务会执行失败(被放弃),EXEC将放弃当前事务中
的所有命令(返回NULL-multi-bulk回复)。
3.因为redis的命令是一条条执行的(单进程模式),所以用WATCH命令是可以做到数据并发处理的,不存在回滚,因为还没有执行的机
会就被放弃掉了。
4.事务中某个操作失败,并不会回滚其他操作。这一点需要注意。
5.我们可以使用discard()方法来取消事务。就是取消事务队列中的所有指令。
命令说明
1.MULTI
用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。
2.EXEC
执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,
那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。
原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。
3.DISCARD
回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将
UNWATCH所有的Keys。
4.WATCH key [key ...]
在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执
行该事务队列中的所有命令。
5.UNWATCH
取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监
控的Keys都将自动取消。
三、管道(Pipelining)
有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。这样可以取得非常好的执行效率。这就是管道
public void test3Pipelined() { Jedis jedis = new Jedis("localhost"); Pipeline pipeline = jedis.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("p" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
四、管道中调用事务
就Jedis提供的方法而言,是可以做到在管道中使用事务
public void test4combPipelineTrans() { jedis = new Jedis("localhost"); 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 void test5shardNormal() { 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("sn" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect(); }
这个是分布式直接连接,并且是同步调用,每步执行都返回执行结果。
六、分布式直连异步调用
public void test6shardpipelined() { 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("sp" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect(); }
七、分布式连接池同步调用
如果,你的分布式调用代码是运行在线程中,那么上面两个直连调用方式就不合适了,因为直连方式是非线程安全的,
这个时候,你就必须选择连接池调用。
public void test7shardSimplePool() { 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("spn" + i, "n" + i); } long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy(); }
八、分布式连接池异步调用
public void test8shardPipelinedPool() { 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("sppn" + i, "n" + 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(); }
JedisPool应用
1.Jedis使用commons-pool完成池化实现。
先做个配置文件(properties文件):
#最大分配的对象数 redis.pool.maxActive=1024 #最大能够保持idel状态的对象数 redis.pool.maxIdle=200 #当池内没有返回对象时,最大等待时间 redis.pool.maxWait=1000 #当调用borrow Object方法时,是否进行有效性检查 redis.pool.testOnBorrow=true #当调用return Object方法时,是否进行有效性检查 redis.pool.testOnReturn=true #IP redis.ip=172.0.0.1 #Port redis.port=6379
jedisPool的相关详细配置可参考: http://www.2cto.com/database/201311/254449.html
在静态代码段中完成初始化:
private static JedisPool pool; static { ResourceBundle bundle = ResourceBundle.getBundle("redis"); if (bundle == null) { throw new IllegalArgumentException("[redis.properties] is not found!"); } JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(Integer.valueOf(bundle.getString("redis.pool.maxActive"))); config.setMaxIdle(Integer.valueOf(bundle.getString("redis.pool.maxIdle"))); config.setMaxWait(Long.valueOf(bundle.getString("redis.pool.maxWait"))); config.setTestOnBorrow(Boolean.valueOf(bundle.getString("redis.pool.testOnBorrow"))); config.setTestOnReturn(Boolean.valueOf(bundle.getString("redis.pool.testOnReturn"))); pool = new JedisPool(config, bundle.getString("redis.ip"), Integer.valueOf(bundle.getString("redis.port"))); }
修改为Jedis从pool中获得:
// 从池中获取一个Jedis对象 Jedis jedis = pool.getResource(); String keys = "name"; // 删数据 jedis.del(keys); // 存数据 jedis.set(keys, "snowolf"); // 取数据 String value = jedis.get(keys); System.out.println(value); // 释放对象池 pool.returnResource(jedis);
Jedis分布式
Jedis分布式(就是数据按key算出hash后按hash分片方式(就是对数据进行分类,方便后面查找)存到不同的redis服务器)
保留前面的 JedisPoolConfig ,新增两个Redis的IP(redis1.ip,redis2.ip),完成两个
JedisShardInfo 实例 ,并将其丢进List中:
JedisShardInfo jedisShardInfo1 = new JedisShardInfo( bundle.getString("redis1.ip"), Integer.valueOf(bundle.getString("redis.port"))); JedisShardInfo jedisShardInfo2 = new JedisShardInfo( bundle.getString("redis2.ip"), Integer.valueOf(bundle.getString("redis.port"))); List<JedisShardInfo> list = new LinkedList<JedisShardInfo>(); list.add(jedisShardInfo1); list.add(jedisShardInfo2);
初始化 ShardedJedisPool 代替 JedisPool:
ShardedJedisPool pool = new ShardedJedisPool(config, list);
改由ShardedJedis,获取Jedis对象:
// 从池中获取一个Jedis对象 ShardedJedis jedis = pool.getResource(); String keys = "name"; String value = "snowolf"; // 删数据 jedis.del(keys); // 存数据 jedis.set(keys, value); // 取数据 String v = jedis.get(keys); System.out.println(v); // 释放对象池 pool.returnResource(jedis);
通过以上方式,向redis进行set操作的key-value,会通过hash而均匀的分配到pool里的redis机器中。
参考原文: http://www.tuicool.com/articles/jM7RF3Y
参考原文: http://www.open-open.com/lib/view/open1410485827242.html
参考原文: http://www.cnblogs.com/stephen-liu74/archive/2012/03/28/2357783.html