您的位置:首页 > 数据库 > Redis

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jedis 事务 管道 redis