1、缓存更新策略
- 利用Redis的缓存淘汰策略被动更新 LRU 、LFU
- 利用TTL被动更新
- 在更新数据库时主动更新 (先更数据库再删缓存----延时双删)
- 异步更新 定时任务 数据不保证时时一致 不穿DB
2、不同策略之间的优缺点
策略 | 一致性 | 维护成本 |
利用Redis的缓存淘汰策略被动更新 | 最差 | 最低 |
利用TTL被动更新 | 较差 | 较低 |
在更新数据库时主动更新 | 较强 | 最高 |
3、Redis与Mybatis整合
- 可以使用Redis做Mybatis的二级缓存,在分布式环境下可以使用。
- 框架采用springboot+Mybatis+Redis。框架的搭建就不赘述了。
3.1、在pom.xml中添加Redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.2、在application.yml中添加Redis配置
#开发配置
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
3.3、缓存实现
ApplicationContextHolder 用于注入RedisTemplate
package com.lagou.rcache.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext ctx;
@Override
//向工具类注入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);
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) ctx.getBean(name);
}
}
RedisCache 使用redis实现mybatis二级缓存
package 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;
}
@Override
public String getId() {
return this.id;
}
//给模板对象RedisTemplate赋值,并传出去
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) { //每个连接池的连接都要获得RedisTemplate
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
/*
保存缓存对象的方法
*/
@Override
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);
}
}
/*
获取缓存对象的方法
*/
@Override
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;
}
}
/*
删除缓存对象
*/
@Override
public Object del(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
System.out.println("删除缓存对象成功!");
} catch (Throwable t) {
System.out.println("删除缓存对象失败!" + t);
}
return null;
}
/*
清空缓存对象
当缓存的对象更新了的化,就执行此方法
*/
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
//回调函数
redisTemplate.execute((RedisCallback) collection -> {
collection.flushDb();
return null;
});
System.out.println("清空缓存对象成功!");
}
//可选实现的方法
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
3.4、在mapper中增加二级缓存开启(默认不开启)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<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>
3.5、在启动时允许缓存
package 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;
@SpringBootApplication
@MapperScan("com.lagou.rcache.dao")
@EnableCaching
public class RcacheApplication {
public static void main(String[] args) {
SpringApplication.run(RcacheApplication.class, args);
}
}
注意:这里我只是介绍了其相关的核心代码,其他部分的代码进行了省略。例如:映射实体类、controller访问调用mybatis。这里只是简要的介绍,你还可以自己实现,最重要的是实现Mybatis的Catch接口。