Redis实战-Jedis使用指南
2016-05-01 23:04
696 查看
概述
Redis是一个开源的、高效的key-value存储系统,也是nosql中的最常见的一种。redis非常适合用来做缓存系统,关于Redis的详细介绍可以查看Redis官方documentation。Redis支持多语言的调用,官方推荐的Java版客户端是Jedis,它非常强大和稳定,支持事务、管道及有Jedis自身实现。我们对Redis数据的操作,都可以通过Jedis来完成。
使用教程
1、配置maven依赖
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> <type>jar</type> <scope>compile</scope> </dependency>
Jedis 线程安全问题
首先,需要注意的是单个的Jedis 实例不是线程安全,在多线程环境下你应该使用JedisPool。using Jedis in a multithreaded environment
You shouldn’t use the same instance from different threads because you’ll have strange errors. And sometimes creating lots of Jedis instances is not good enough because it means lots of sockets and connections, which leads to strange errors as well. A single Jedis instance is not threadsafe! To avoid these problems, you should use JedisPool, which is a threadsafe pool of network connections. You can use the pool to reliably create several Jedis instances, given you return the Jedis instance to the pool when done. This way you can overcome those strange errors and achieve great performance.
初始化pool
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
JedisPool 是线程安全的,你可以将它作为一个静态变量保存起来。
为了保证Jedis 一定会被关闭,我们可以使用try-finally语句,如下:
Jedis jedis = null; try { jedis = pool.getResource(); /// ... do stuff here ... for example jedis.set("foo", "bar"); String foobar = jedis.get("foo"); jedis.zadd("sose", 0, "car"); jedis.zadd("sose", 0, "bike"); Set<String> sose = jedis.zrange("sose", 0, -1); } finally { if (jedis != null) { jedis.close(); } } /// ... when closing your application: pool.destroy();
在介绍Jedis API使用之前,我们先使用单例模式对JedisPool 做一个封装,代码如下:
package com.ricky.codelab.redis.sample.pool; import java.io.IOException; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import com.ricky.codelab.redis.sample.util.PropertyUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisPoolManager { private volatile static JedisPoolManager manager; private final JedisPool pool; private JedisPoolManager() { try { //加载redis配置 Properties props = PropertyUtils.load("redis.properties"); // 创建jedis池配置实例 JedisPoolConfig config = new JedisPoolConfig(); // 设置池配置项值 String maxTotal = props.getProperty("redis.pool.maxTotal", "4"); config.setMaxTotal(Integer.parseInt(maxTotal)); String maxIdle = props.getProperty("redis.pool.maxIdle", "4"); config.setMaxIdle(Integer.parseInt(maxIdle)); String minIdle = props.getProperty("redis.pool.minIdle", "1"); config.setMinIdle(Integer.parseInt(minIdle)); String maxWaitMillis = props.getProperty("redis.pool.maxWaitMillis", "1024"); config.setMaxWaitMillis(Long.parseLong(maxWaitMillis)); String testOnBorrow = props.getProperty("redis.pool.testOnBorrow", "true"); config.setTestOnBorrow("true".equals(testOnBorrow)); String testOnReturn = props.getProperty("redis.pool.testOnReturn", "true"); config.setTestOnReturn("true".equals(testOnReturn)); String server = props.getProperty("redis.server"); if(StringUtils.isEmpty(server)){ throw new IllegalArgumentException("JedisPool redis.server is empty!"); } String[] host_arr = server.split(","); if(host_arr.length>1){ throw new IllegalArgumentException("JedisPool redis.server length > 1"); } String[] arr = host_arr[0].split(":"); // 根据配置实例化jedis池 System.out.println("***********init JedisPool***********"); System.out.println("host->"+arr[0]+",port->"+arr[1]); pool = new JedisPool(config, arr[0], Integer.parseInt(arr[1])); } catch (IOException e) { throw new IllegalArgumentException("init JedisPool error", e); } } public static JedisPoolManager getMgr() { if (manager == null) { synchronized (JedisPoolManager.class) { if (manager == null) { manager = new JedisPoolManager(); } } } return manager; } public Jedis getResource() { return pool.getResource(); } public void destroy() { // when closing your application: pool.destroy(); } public void close() { pool.close(); } }
redis.properties 如下:
# Redis server ip and port redis.server=172.18.19.208:6379 # Redis pool redis.pool.maxTotal=20 redis.pool.maxIdle=10 redis.pool.minIdle=1 redis.pool.maxWaitMillis=60000 redis.pool.testOnBorrow=true redis.pool.testOnReturn=true
有了JedisPoolManager类,操作Jedis的模板代码简化如下:
Jedis jedis = null; try { jedis = JedisPoolManager.getMgr().getResource(); // jedis.auth("hello"); } finally { if (jedis != null) { jedis.close(); } } /// ... when closing your application: JedisPoolManager.getMgr().destroy();
基本用法
package com.ricky.codelab.redis.sample; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.ricky.codelab.redis.sample.pool.JedisPoolManager; import redis.clients.jedis.Jedis; public class RedisDemo { public static void main(String[] args) { Jedis jedis = null; try { jedis = JedisPoolManager.getMgr().getResource(); // jedis.auth("hello"); //simple key-value jedis.set("redis", "myredis"); System.out.println(jedis.get("redis")); jedis.append("redis", "yourredis"); jedis.append("mq", "RabbitMQ"); //incr String pv = jedis.set("pv", "0"); System.out.println("pv:"+pv); jedis.incr("pv"); jedis.incrBy("pv", 10); System.out.println("pv:"+pv); //mset jedis.mset("firstName", "ricky", "lastName", "Fung"); System.out.println(jedis.mget("firstName", "lastName")); //map Map<String,String> cityMap = new HashMap<String,String>(); cityMap.put("beijing", "1"); cityMap.put("shanghai", "2"); jedis.hmset("city", cityMap); System.out.println(jedis.hget("city", "beijing")); System.out.println(jedis.hlen("city")); System.out.println(jedis.hmget("city", "beijing","shanghai")); //list jedis.lpush("hobbies", "reading"); jedis.lpush("hobbies", "basketball"); jedis.lpush("hobbies", "shopping"); List<String> hobbies = jedis.lrange("hobbies", 0, -1); System.out.println("hobbies:"+hobbies); jedis.del("hobbies"); //set jedis.sadd("name", "ricky"); jedis.sadd("name", "kings"); jedis.sadd("name", "demon"); System.out.println("size:"+jedis.scard("name")); System.out.println("exists:"+jedis.sismember("name", "ricky")); System.out.println(String.format("all members: %s", jedis.smembers("name"))); System.out.println(String.format("rand member: %s", jedis.srandmember("name"))); //remove jedis.srem("name", "demon"); //hset jedis.hset("address", "country", "CN"); jedis.hset("address", "province", "BJ"); jedis.hset("address", "city", "Beijing"); jedis.hset("address", "district", "Chaoyang"); System.out.println("city:"+jedis.hget("address", "city")); System.out.println("keys:"+jedis.hkeys("address")); System.out.println("values:"+jedis.hvals("address")); //zadd jedis.zadd("gift", 0, "car"); jedis.zadd("gift", 0, "bike"); Set<String> gift = jedis.zrange("gift", 0, -1); System.out.println("gift:"+gift); } finally { if (jedis != null) { jedis.close(); } } /// ... when closing your application: JedisPoolManager.getMgr().destroy(); } }
另外,我们除了可以使用redis.clients.jedis.Jedis.set(String key, String value) insert string之外,还可以使用redis.clients.jedis.BinaryJedis.set(byte[] key, byte[] value) 保存我们自定义的POJO类,代码如下:
package com.ricky.codelab.redis.sample; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import com.ricky.codelab.redis.sample.pool.JedisPoolManager; import redis.clients.jedis.Jedis; public class RedisPojoDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { Jedis jedis = null; try { jedis = JedisPoolManager.getMgr().getResource(); Person person = new Person("Ricky", 27); //序列化 byte[] byteArray = serialize(person); //set jedis.set("Ricky".getBytes(), byteArray); //get byteArray = jedis.get("Ricky".getBytes()); //反序列化 person = deserialize(byteArray); System.out.println(person); } finally { if (jedis != null) { jedis.close(); } } /// ... when closing your application: JedisPoolManager.getMgr().destroy(); } public static Person deserialize(byte[] byteArray) throws ClassNotFoundException, IOException{ ObjectInputStream ois = null; try { ByteArrayInputStream bais = new ByteArrayInputStream(byteArray); ois = new ObjectInputStream(bais); return (Person) ois.readObject(); } finally { ois.close(); } } public static byte[] serialize(Person person) throws IOException{ ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(person); oos.flush(); return baos.toByteArray(); } finally { oos.close(); baos.close(); } } } class Person implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
这里需要用到Java的序列化机制,我们使用的Java内置的序列化机制,当然也可以使用第三方提供的高效序列化机制,例如:Kryo、Hessian、protostuff等。
高级用法
1、事务(Transactions)所谓事务,即一个连续操作,是否执行是一个事务,要么完成,要么失败,没有中间状态。
而redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令,也就是事务的连贯性。
Jedis jedis = null; try { jedis = JedisPoolManager.getMgr().getResource(); jedis.auth("password"); Transaction t = jedis.multi(); t.set("foo", "bar"); t.exec(); } finally { if (jedis != null) { jedis.close(); } } /// ... when closing your application: JedisPoolManager.getMgr().destroy();
我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。
如果某些方法有返回值,你可以这么做:
Transaction t = jedis.multi(); t.set("fool", "bar"); Response<String> result1 = t.get("fool"); t.zadd("foo", 1, "barowitch"); t.zadd("foo", 0, "barinsky"); t.zadd("foo", 0, "barikoviev"); Response<Set<String>> sose = t.zrange("foo", 0, -1); // get the entire sortedset t.exec(); // dont forget it String foolbar = result1.get(); // use Response.get() to retrieve things from a Response int soseSize = sose.get().size(); // on sose.get() you can directly call Set methods! // List<Object> allResults = t.exec(); // you could still get all results at once, as before
2、管道(Pipelining)
Sometimes you need to send a bunch of different commands. A very cool way to do that, and have better performance than doing it the naive way, is to use pipelining. This way you send commands without waiting for response, and you actually read the responses at the end, which is faster.
Pipeline p = jedis.pipelined(); p.set("fool", "bar"); p.zadd("foo", 1, "barowitch"); p.zadd("foo", 0, "barinsky"); p.zadd("foo", 0, "barikoviev"); Response<String> pipeString = p.get("fool"); Response<Set<String>> sose = p.zrange("foo", 0, -1); p.sync(); int soseSize = sose.get().size(); Set<String> setBack = sose.get();
3、Publish/Subscribe
To subscribe to a channel in Redis, create an instance of JedisPubSub and call subscribe on the Jedis instance:
class MyListener extends JedisPubSub { public void onMessage(String channel, String message) { } public void onSubscribe(String channel, int subscribedChannels) { } public void onUnsubscribe(String channel, int subscribedChannels) { } public void onPSubscribe(String pattern, int subscribedChannels) { } public void onPUnsubscribe(String pattern, int subscribedChannels) { } public void onPMessage(String pattern, String channel, String message) { } } MyListener l = new MyListener(); jedis.subscribe(l, "foo");
4、分布式Redis(ShardedJedis)
1.Define your shards:
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(); JedisShardInfo si = new JedisShardInfo("localhost", 6379); si.setPassword("foobared"); shards.add(si); si = new JedisShardInfo("localhost", 6380); si.setPassword("foobared"); shards.add(si);
2.a) Direct connection:
ShardedJedis jedis = new ShardedJedis(shards); jedis.set("a", "foo"); jedis.disconnect();
2.b) Pooled connection:
ShardedJedisPool pool = new ShardedJedisPool(new Config(), shards); ShardedJedis jedis = pool.getResource(); jedis.set("a", "foo"); .... // do your work here pool.returnResource(jedis); .... // a few moments later ShardedJedis jedis2 = pool.getResource(); jedis.set("z", "bar"); pool.returnResource(jedis); pool.destroy();
最后,我对ShardedJedisPool 也做了一个简单封装,从redis_cluster.properties 文件中获取配置信息并做初始化,代码如下:
package com.ricky.codelab.redis.sample.pool; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import com.ricky.codelab.redis.sample.util.PropertyUtils; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; import redis.clients.jedis.ShardedJedisPool; public class ShardedJedisPoolManager { private volatile static ShardedJedisPoolManager manager; private final ShardedJedisPool shardedJedisPool; private ShardedJedisPoolManager() { try { Properties props = PropertyUtils.load("redis_cluster.properties"); // 创建jedis池配置实例 JedisPoolConfig config = new JedisPoolConfig(); // 设置池配置项值 String maxTotal = props.getProperty("redis.pool.maxTotal", "4"); config.setMaxTotal(Integer.parseInt(maxTotal)); String maxIdle = props.getProperty("redis.pool.maxIdle", "4"); config.setMaxIdle(Integer.parseInt(maxIdle)); String minIdle = props.getProperty("redis.pool.minIdle", "1"); config.setMinIdle(Integer.parseInt(minIdle)); String maxWaitMillis = props.getProperty("redis.pool.maxWaitMillis", "1024"); config.setMaxWaitMillis(Long.parseLong(maxWaitMillis)); String testOnBorrow = props.getProperty("redis.pool.testOnBorrow", "true"); config.setTestOnBorrow("true".equals(testOnBorrow)); String testOnReturn = props.getProperty("redis.pool.testOnReturn", "true"); config.setTestOnReturn("true".equals(testOnReturn)); String server = props.getProperty("redis.server"); if(StringUtils.isEmpty(server)){ throw new IllegalArgumentException("ShardedJedisPool redis.server is empty!"); } String[] host_arr = server.split(","); List<JedisShardInfo> list = new ArrayList<JedisShardInfo>(host_arr.length); for(String host : host_arr){ String[] arr = host.split(":"); System.out.println("init ShardedJedisPool host->"+arr[0]+",port->"+arr[1]); JedisShardInfo jedisShardInfo = new JedisShardInfo( arr[0], Integer.parseInt(arr[1])); jedisShardInfo.setPassword("password"); list.add(jedisShardInfo); } //根据配置文件,创建shared池实例 System.out.println("***********init ShardedJedisPool***********"); shardedJedisPool = new ShardedJedisPool(config, list); } catch (IOException e) { throw new IllegalArgumentException("init ShardedJedisPool error", e); } } public static ShardedJedisPoolManager getMgr() { if (manager == null) { synchronized (ShardedJedisPoolManager.class) { if (manager == null) { manager = new ShardedJedisPoolManager(); } } } return manager; } public ShardedJedis getResource() { return shardedJedisPool.getResource(); } public void destroy() { // when closing your application: shardedJedisPool.destroy(); } public void close() { shardedJedisPool.close(); } }
redis_cluster.properties
# Redis server ip and port redis.server=172.18.19.206:6379,172.18.19.207:6379,172.18.19.208:6379 password=12345 # Redis pool redis.pool.maxTotal=20 redis.pool.maxIdle=10 redis.pool.minIdle=1 redis.pool.maxWaitMillis=60000 redis.pool.testOnBorrow=true redis.pool.testOnReturn=true
点此下载完整示例代码
参考资料:
https://github.com/xetorthio/jedis/wiki
相关文章推荐
- redis安装问题小结
- 使用 Redis 和 Python 构建一个共享单车的应用程序
- Redis偶发连接失败案例实战记录
- Redis中实现查找某个值的范围
- redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)
- Redis和Memcached的区别详解
- 分割超大Redis数据库例子
- Redis总结笔记(一):安装和常用命令
- Redis sort 排序命令详解
- 用Redis实现微博关注关系
- Redis实现信息已读未读状态提示
- redis中修改配置文件中的端口号 密码方法
- 在Ruby on Rails上使用Redis Store的方法
- SQL Server误区30日谈 第1天 正在运行的事务在服务器故障转移后继续执行
- 浅析SQL Server中包含事务的存储过程
- Mysql中的事务是什么如何使用
- MySql的事务使用与示例详解
- Redis和Memcache的区别总结
- C#分布式事务的超时处理实例分析
- C#中的事务用法实例分析