Redis做mybatis二级缓存

Redis作redis的二级缓存

一、搭建redis

docker run   --name redis-6379 -p 6379:6379 --restart=always --name redis -d redis --requirepass "123456"

二、依赖

<!-- database start -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.12</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.9</version>
</dependency>
<!-- database end -->

三、application.yml

spring:
  # 数据源配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://url/database   characterEncoding=utf8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    username: root
    password: 123456

  # redis设置
  redis:
    database: 0 # Redis 数据库索引(默认为 0)
    host: 192.168.37.139 # Redis 服务器地址
    port: 6379 # Redis 服务器连接端口
    password: 123456 # Redis 服务器连接密码(默认为空)
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-idle: 8 # 连接池中的最大空闲连接 默认 8
        min-idle: 0 # 连接池中的最小空闲连接 默认 0

四、相关类

package com.sunyo.wlpt.message.bus.service.cache;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.DigestUtils;

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


/**
 * @author 子诚
 * Description:自定义Redis作为mybatis二级缓存实现
 * 问题描述:缓存穿透、缓存雪崩
 * <p>
 * 缓存穿透:就是说利用一些列措施,使得访问避开了缓存,直接访问数据库,使得数据库不堪重负引起的问题。比如(压测)访问数据库中不存在的数据
 * 解决方案:读取数据库,不存在;依旧生成对应的key,放到缓存中,但是对应的value是null(mybatis的二级缓存是这样解决的)。
 * 下次再次访问的话,就是读取缓存。
 * <p>
 * 缓存雪崩:是指在某一特殊时刻,缓存中的缓存全部失效,然后这一时刻又有大量的数据库访问,导致数据库不堪重负。
 * 解决方案:根据业务的不同设置不同的缓存失效时间。
 * 比如:这个项目,做了3个namespace的缓存,其中一个namespace,共有5个Mapper指向它。所以选择使用范围内的随机值,来做缓存失效时间
 * <p>
 * 时间:2020/8/6 9:37
 */
public class RedisCache implements Cache {

    /**
     * slf4j的日志记录器
     */
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    /**
     * 用于事务性缓存操作的读写锁
     */
    private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 缓存对象的是失效时间,30分钟
     */
    private static final long CACHE_TIME_IN_MINT = 30;

    /**
     * 当前放入缓存的mapper的namespace
     */
    private final String id;

    /**
     * 必须存在构造方法
     */
    public RedisCache(String id)
    {
        this.id = id;
    }

    /**
     * 返回cache唯一标识
     */
    @Override
    public String getId()
    {
        return this.id;
    }

    /**
     * 缓存放入值  redis RedisTemplate   StringRedisTemplate
     *
     * @param key   hash_key
     * @param value hash_value
     */
    @Override
    public void putObject(Object key, Object value)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        // 使用redis的hash类型作为缓存存储模型
        redisTemplate.opsForHash().put(id.toString(), encryptionKey(key.toString()), value);
        // 设置缓存时间
        redisTemplate.expire(id.toString(), CACHE_TIME_IN_MINT, TimeUnit.MINUTES);
    }

    /**
     * 缓存中中获取数据
     */
    @Override
    public Object getObject(Object key)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        //根据key 从redis的hash类型中获取数据
        return redisTemplate.opsForHash().get(id.toString(), encryptionKey(key.toString()));
    }

    /**
     * 注意:这个方法为mybatis保留方法 默认没有实现 后续版本可能会实现
     *
     * @param key hash_key
     * @return
     */
    @Override
    public Object removeObject(Object key)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.delete(key);
        return null;
    }

    @Override
    public void clear()
    {
        logger.info("清空->{}<-缓存", id);
        RedisTemplate redisTemplate = getRedisTemplate();
        // 清空 namespace
        redisTemplate.delete(id.toString());
    }

    /**
     * 用来计算缓存数量
     */
    @Override
    public int getSize()
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        // 获取hash中key value数量
        return redisTemplate.opsForHash().size(id.toString()).intValue();
    }

    /**
     * 封装redisTemplate
     *
     * @return RedisTemplate
     */
    private RedisTemplate getRedisTemplate()
    {
        //通过application工具类获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    /**
     * 封装一个对key进行md5处理方法
     */
    private String encryptionKey(String key)
    {
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }

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

}



package com.zicheng.cache;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.DigestUtils;

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


/**
 * @author 子诚
 * Description:自定义Redis作为mybatis二级缓存实现
 * 时间:2020/8/6 9:37
 */
public class RedisCache implements Cache {

    /**
     * slf4j的日志记录器
     */
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    /**
     * 用于事务性缓存操作的读写锁
     */
    private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 缓存对象的是失效时间,30分钟
     */
    private static final long CACHE_TIME_IN_MINT = 30;

    /**
     * 当前放入缓存的mapper的namespace
     */
    private final String id;

    /**
     * 必须存在构造方法
     */
    public RedisCache(String id)
    {
        this.id = id;
    }

    /**
     * 返回cache唯一标识
     */
    @Override
    public String getId()
    {
        return this.id;
    }

    /**
     * 缓存放入值  redis RedisTemplate   StringRedisTemplate
     *
     * @param key   hash_key
     * @param value hash_value
     */
    @Override
    public void putObject(Object key, Object value)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        // 使用redis的hash类型作为缓存存储模型
        redisTemplate.opsForHash().put(id.toString(), key.toString(), value);
        redisTemplate.expire(id.toString(), CACHE_TIME_IN_MINT, TimeUnit.MINUTES);
    }

    /**
     * 缓存中中获取数据
     */
    @Override
    public Object getObject(Object key)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        //根据key 从redis的hash类型中获取数据
        return redisTemplate.opsForHash().get(id.toString(), key.toString());
    }

    /**
     * 注意:这个方法为mybatis保留方法 默认没有实现 后续版本可能会实现
     *
     * @param key hash_key
     * @return
     */
    @Override
    public Object removeObject(Object key)
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.delete(key);
        return null;
    }

    @Override
    public void clear()
    {
        logger.info("清空->{}<-缓存", id);
        RedisTemplate redisTemplate = getRedisTemplate();
        // 清空 namespace
        redisTemplate.delete(id.toString());
    }

    /**
     * 用来计算缓存数量
     */
    @Override
    public int getSize()
    {
        RedisTemplate redisTemplate = getRedisTemplate();
        // 获取hash中key value数量
        return redisTemplate.opsForHash().size(id.toString()).intValue();
    }

    /**
     * 封装redisTemplate
     *
     * @return RedisTemplate
     */
    private RedisTemplate getRedisTemplate()
    {
        //通过application工具类获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    /**
     * 封装一个对key进行md5处理方法
     */
    private String getKeyToMD5(String key)
    {
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }

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

}

五、开启二级缓存

在mybatis的Mapper.xml中

<!-- 开启二级缓存 -->
<cache type="com.zicheng.cache.RedisCache"/>
<!-- 关联其他的namespace -->
<cache-ref namespace="com.zicheng.UserMapper""/>

关联其他的namespace:

比如 工资Mapper关联了员工Mapper:员工表执行增删改操作,

不仅员工Mapper(namespace)的缓存清空,工资Mapper的缓存(namespace)也清空

atis的Mapper.xml中

<!-- 开启二级缓存 -->
<cache type="com.zicheng.cache.RedisCache"/>
<!-- 关联其他的namespace -->
<cache-ref namespace="com.zicheng.UserMapper""/>

关联其他的namespace:

比如 工资Mapper关联了员工Mapper:员工表执行增删改操作,

不仅员工Mapper(namespace)的缓存清空,工资Mapper的缓存(namespace)也清空

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值