Redis企业缓存与数据库一致性
缓存更新策略
利用Redis的缓存淘汰策略被动更新 LRU 、LFU
利用TTL被动更新
在更新数据库时主动更新 (先更数据库再删缓存----延时双删)
异步更新 定时任务 数据不保证时时一致 不穿DB
不同策略之间的优缺点
策略 | 一致性 | 维护成本 |
---|---|---|
利用Redis的缓存淘汰策略被动更新 | 最差 | 最低 |
利用TTL被动更新 | 较差 | 较低 |
在更新数据库时主动更新 | 较强 | 最高 |
与Mybatis整合
可以使用Redis做Mybatis的二级缓存,在分布式环境下可以使用。
框架采用springboot+Mybatis+Redis。
-
- 在pom.xml中添加Redis依赖
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> -
- 在application.yml中添加Redis配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#开发配置
spring: #数据源配置
datasource:
url: jdbc:mysql://192.168.127.128:3306/test? serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
redis:
host: 192.168.127.128
port: 6379
jedis:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms #公共配置与profiles选择无关
mybatis:
typeAliasesPackage: com.lagou.rcache.entity
mapperLocations: classpath:mapper/*.xml -
- 缓存实现
ApplicationContextHolder 用于注入RedisTemplate
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
32package com.lagou.rcache.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext ctx;
//向工具类注入applicationContext
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext; //ctx就是注入的applicationContext
}
//外部调用ctx
public static ApplicationContext getCtx() {
return ctx;
}
public static <T> T getBean(Class<T> tClass) {
return ctx.getBean(tClass);
}
public static <T> T getBean(String name) {
return (T) ctx.getBean(name);
}
}RedisCache 使用redis实现mybatis二级缓存
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
116package com.lagou.rcache.utils;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 使用redis实现mybatis二级缓存
*/
public class RedisCache implements Cache {
//缓存对象唯一标识
private final String id; //orm的框架都是按对象的方式缓存,而每个对象都需要一个唯一标识
//用于事务性缓存操作的读写锁
private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//处理事务性缓存中做的 //操作数据缓存的--跟着线程走的
private RedisTemplate redisTemplate; //Redis的模板负责将缓存对象写到redis服务器里面去
//缓存对象的是失效时间,30分钟
private static final long EXPRIRE_TIME_IN_MINUT = 30;
//构造方法---把对象唯一标识传进来
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("缓存对象id是不能为空的");
}
this.id = id;
}
public String getId() {
return this.id;
}
//给模板对象RedisTemplate赋值,并传出去
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
//每个连接池的连接都要获得
RedisTemplate redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
/* 保存缓存对象的方法 */
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
//使用redisTemplate得到值操作对象
ValueOperations operation = redisTemplate.opsForValue();
//使用值操作对象operation设置缓存对象
operation.set(key, value, EXPRIRE_TIME_IN_MINUT, TimeUnit.MINUTES);
//TimeUnit.MINUTES系统当前时间的分钟数
System.out.println("缓存对象保存成功");
} catch (Throwable t) {
System.out.println("缓存对象保存失败" + t);
}
}
/* 获取缓存对象的方法 */
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations operations = redisTemplate.opsForValue();
Object result = operations.get(key);
System.out.println("获取缓存对象");
return result;
} catch (Throwable t) {
System.out.println("缓存对象获取失败" + t);
return null;
}
}
/* 删除缓存对象 */
public Object del(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
System.out.println("删除缓存对象成功!");
} catch (Throwable t) {
System.out.println("删除缓存对象失败!" + t);
}
return null;
}
/* 清空缓存对象 当缓存的对象更新了的化,就执行此方法 */
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
//回调函数
redisTemplate.execute((RedisCallback) collection -> {
collection.flushDb();
return null;
});
System.out.println("清空缓存对象成功!");
}
//可选实现的方法
public int getSize() {
return 0;
}
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
} -
- 在mapper中增加二级缓存开启(默认不开启)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<mapper namespace="com.lagou.rcache.dao.UserDao" >
<cache type="com.lagou.rcache.utils.RedisCache" />
<resultMap id="BaseResultMap" type="com.lagou.rcache.entity.TUser" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="address" property="address" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, name, address
</sql>
<select id="selectUser" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from tuser
</select>
</mapper> -
- 在启动时允许缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.lagou.rcache;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
public class RcacheApplication {
public static void main(String[] args) {
SpringApplication.run(RcacheApplication.class, args);
}
} -
- 调用controller的方法
运行结果:
控制台:
Redis客户端:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 WeiJia_Rao!