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 (业务模块)设置不同的有效期