Springboot集成redis实现Mybatis的二级缓存
二级缓存的作用范围为当前的namespace,如果为两张表的二级缓存,可以使用
导入依赖
<!--Stringboot集成redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--自定义redistemplate实现方式用到-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
配置redis链接参数
# 服务基本配置
server.port=8888
server.servlet.context-path=/
# 文件上传
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.location=/Users/admin/Desktop/uploadTmpDir
# post表单提交乱码
spring.http.encoding.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
# 数据源
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
## MyBatis配置信息
mybatis.type-aliases-package=com.baizhi.entities
mybatis.mapper-locations=classpath*:mappers/*.xml
mybatis.executor-type=batch
# 配置Redis链接
spring.redis.host=CentOS
spring.redis.port=6379
spring.redis.timeout=5s
spring.redis.lettuce.shutdown-timeout=100ms
spring.redis.lettuce.pool.max-active=10
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=5ms
spring.redis.lettuce.pool.min-idle=1
启动redis
[root@centos bin]# ./redis-server /root/redis-4.0.10/redis.conf
[root@centos bin]# ps -aux|grep redis
[root@centos bin]# ./redis-cli -h centos -p 6379
centos:6379>
关闭redis的server
[root@centos bin]# ./redis-cli shutdown
配置自定义缓存方式
在mapper.xml文件中指定自定义的 cache 实现类,取消默认的mybatis缓存实现
<cache type="com.baizhi.cache.RedisCacahe"></cache>
在自定义实现的cache类中必须给一个id的有参构造并实现其中的方法,否则会出现 init 异常
public class RedisCacahe implements Cache {
//引入日志管理
private Logger logger= LoggerFactory.getLogger(RedisCacahe.class);
//id是传递过来的namespace(例:com.baizhi.dao.UserDao)
private String id;
//引入redisTemplate对象,从RedisAutoConfiguration中获取默认的
private RedisTemplate redisTemplate=(RedisTemplate) RedisCacheAware.getByName("redisTemplate");
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
//需要额外自定义一个带参构造
public RedisCacahe(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
//日志显示
logger.debug("将存储结果存储到cache.key:"+key+",value:"+value);
HashOperations opsForHash = redisTemplate.opsForHash();
opsForHash.put(id,key,value);
}
@Override
public Object getObject(Object key) {
HashOperations opsForHash = redisTemplate.opsForHash();
Object value = opsForHash.get(id,key);
logger.debug("从缓存中获取存储结果:"+value);
return value;
}
@Override
public Object removeObject(Object o) {
return null;
}
@Override
public void clear() {
logger.debug("清空缓存区中的数据");
redisTemplate.delete(id);
}
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
要使用redis的完成二级缓存,需要将拿到redis的client,直接注入RedisTemplate不行,因为我们自定义的类并不是由Spring工厂管理的对象。
所以我们需要将自定义的类交由Spring工厂管理,完成依赖注入 —spring给我们提供的后门
//触发器 当spring工厂加载完毕后,调用当前类中的setApplicationContext方法 将工厂对象以参数的形式传递过来
@Component
public class RedisCacheAware implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public static Object getByName(String name){
Object bean = applicationContext.getBean(name);
return bean;
}
public static Object getByClazz(Class clazz){
Object bean = applicationContext.getBean(clazz);
return bean;
}
}
测试
@SpringBootTest(classes = UserApp.class) //声明当前类为springboot的测试类 并且指定入口类
@RunWith(value = SpringRunner.class) // 在容器环境下启动测试
public class UserServiceTest {
@Autowired
IUserService userService;
@Test
public void testUserService() {
User user = new User(1,"a",true,"1",new Date(),"1","1");
User user1 = userService.queryUserById(7);
System.out.println(user1);
}
@Test
public void testSaveUser() {
User user = new User("赵小六",true,"1",new Date(),"1","1");
userService.saveUser(user);
}
}
redis实现的缓存
缓存穿透
外部多次访问数据库中没有的数据,这样的后果是大批量的请求越过缓存直接访问数据库
解决方式:使用布隆过滤,对于不存在的数据直接返回 null 即可。
缓存击穿
数据库中的某条数据访问频次特别高,一但某一时刻此数据的缓存过期,在瞬间有大量请求访问数据库
解决方式:热点数据永不过期
缓存雪崩
不同的 namespace 中的缓存同时过期失效,瞬间有大量的请求访问数据库
解决方式:不同的 namespce (业务模块)设置不同的有效期