Redis学习笔记

基本概念(搬运)

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

入门

Redis的安装与配置

Windows下载地址

直接解压进入,当前位置打开cmd

1
redis-server.exe redis.windows.conf

看见如下界面,启动成功
启动成功

访问redis需要另起一个cmd窗口,前面的服务窗口不要关闭

1
2
3
4
5
6
7
8
9
redis-cli.exe (-h 127.0.0.1 -p 6379)
括号内可以省略

127.0.0.1:6379> set yourKey yourValue

127.0.0.1:6379> get yourKey
此时默认使用的是string的数据结构
所以得到返回值
127.0.0.1:6379> "yourValue"

Redis的数据结构

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

在Java应用中使用Redis

Jedis实现

参考项目:
Redis在Java应用中的测试


SpringMVC+Mybatis整合Redis做二级缓存

第一步当然是引入依赖包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Redis -->
<properties>
<jedis.version>2.9.0</jedis.version>
</properties>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.6.RELEASE</version>
</dependency>

接下来需要创建几个Redis的Util类

SerializeUtil.Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package com.pace2car.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
* 序列化与反序列化Util类(实体类必须实现序列化接口)
* @author Pace2Car
* @date 2018/12/21 10:08
*/
public class SerializeUtil {
private static Logger log = LoggerFactory.getLogger(SerializeUtil.class);
public static byte[] serialize(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
byte[] bytes = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
bytes = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
if (baos != null) {
baos.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return bytes;
}

/**
* 反序列化对象
* @param bytes
* @return
*/
public static Object unserialize(byte[] bytes) {
Object obj = null;
ByteArrayInputStream bais = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
ois.close();
bais.close();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}

/**
* 关闭的数据源或目标。调用 close()方法可释放对象保存的资源(如打开文件)
* 关闭此流并释放与此流关联的所有系统资源。如果已经关闭该流,则调用此方法无效。
* @param closeable
*/
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
log.info("Unable to close %s", closeable, e);
}
}
}

/**
* 列表序列化(用于Redis整存整取)
* @param value
* @return
*/
public static <T> byte[] serialize(List<T> value) {
if (value == null) {
throw new NullPointerException("Can't serialize null");
}
byte[] rv=null;
ByteArrayOutputStream bos = null;
ObjectOutputStream os = null;
try {
bos = new ByteArrayOutputStream();
os = new ObjectOutputStream(bos);
for(T obj : value){
os.writeObject(obj);
}
os.writeObject(null);
os.close();
bos.close();
rv = bos.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
} finally {
close(os);
close(bos);
}
return rv;
}

/**
* 反序列化列表(用于Redis整存整取)
* @param in
* @return
*/
public static <T> List<T> unserializeForList(byte[] in) {
List<T> list = new ArrayList<T>();
ByteArrayInputStream bis = null;
ObjectInputStream is = null;
try {
if(in != null) {
bis=new ByteArrayInputStream(in);
is=new ObjectInputStream(bis);
while (true) {
T obj = (T) is.readObject();
if(obj == null){
break;
}else{
list.add(obj);
}
}
is.close();
bis.close();
}
} catch (IOException e) {
log.warn("Caught IOException decoding %d bytes of data",
in == null ? 0 : in.length, e);
} catch (ClassNotFoundException e) {
log.warn("Caught CNFE decoding %d bytes of data",
in == null ? 0 : in.length, e);
} finally {
close(is);
close(bis);
}
return list;
}
}

RedisCacheTransfer.Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.pace2car.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

/**
* 完成RedisCache.jedisConnectionFactory的静态注入
* @author Pace2Car
* @date 2018/12/21 10:06
*/
public class RedisCacheTransfer {
@Autowired
public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
}
}

RedisCache.Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package com.pace2car.redis;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* 缓存实现类
* @author Pace2Car
* @date 2018/12/21 10:11
*/
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

private static JedisConnectionFactory jedisConnectionFactory;

private final String id;

/**
* The {@code ReadWriteLock}.
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug("MybatisRedisCache:id=" + id);
this.id = id;
}

@Override
public void clear() {
JedisConnection connection = null;
try {
//连接清除数据
connection = (JedisConnection) jedisConnectionFactory.getConnection();
connection.flushDb();
connection.flushAll();
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}

@Override
public String getId() {
return this.id;
}

@Override
public Object getObject(Object key) {
Object result = null;
JedisConnection connection = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
//借用spring_data_redis.jar中的JdkSerializationRedisSerializer.class
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
//利用其反序列化方法获取值
result = serializer.deserialize(connection.get(serializer.serialize(key)));
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}

@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}

@Override
public int getSize() {
int result = 0;
JedisConnection connection = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
result = Integer.valueOf(connection.dbSize().toString());
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}

@Override
public void putObject(Object key, Object value) {
JedisConnection connection = null;
try {
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>putObject:" + key + "=" + value);
connection = (JedisConnection) jedisConnectionFactory.getConnection();
//借用spring_data_redis.jar中的JdkSerializationRedisSerializer.class
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
//利用其序列化方法将数据写入redis服务的缓存中
connection.set(serializer.serialize(key), serializer.serialize(value));

} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}

@Override
public Object removeObject(Object key) {
JedisConnection connection = null;
Object result = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
result = connection.expire(serializer.serialize(key), 0);
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}

public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.jedisConnectionFactory = jedisConnectionFactory;
}
}

配置完成后需要在mybatis配置中添加缓存支持

1
2
3
4
5
6
7
8
9
10
11
<!--设置mybaits对redis缓存的支持-->
<property name="configurationProperties">
<props>
<!-- 全局映射器启用缓存 *主要将此属性设置完成即可-->
<prop key="cacheEnabled">true</prop>
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<prop key="lazyLoadingEnabled">false</prop>
<!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
<prop key="aggressiveLazyLoading">true</prop>
</props>
</property>

使用缓存

使用时需在mapper.xml中开启缓存

1
2
3
4
5
<!-- LRU垃圾数据清理算法,默认为LRU 最近最少使用 -->
<cache eviction="LRU" type="com.pace2car.redis.RedisCache" />

<!-- 在语句上开启 useCache="true" -->
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" useCache="true" >

写测试类测试查询后,在redis客户端看见有新的key添加进来

写入redis的缓存

实体类加上serialVersionUID

serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

1
private static final long serialVersionUID = 3404843471959911386L;
至此Redis便可作为二级缓存使用啦,后续会放上Redis集群的配置,进一步提高系统的性能。

参考项目:

Redis做二级缓存参考项目

热评文章