概述
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 portredis.server=172.18.19.208:6379# Redis poolredis.pool.maxTotal=20redis.pool.maxIdle=10redis.pool.minIdle=1redis.pool.maxWaitMillis=60000redis.pool.testOnBorrow=trueredis.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)
所謂事務,即一個連續操作,是否