SpringBoot+Mybatis整合redis

1.整合流程图

 2、SpringBoot+Mybatis整合redis

2.1 创建springboot+mybatis项目,引入jar包

<!--springboot整合redis jar  开始-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<!--springboot整合redis jar  结束-->

 2.2配置连接redis数据源

把数据单独配置,防止application.properties/.yml 配置过于臃肿,防止硬编码

加上一个配置 project.properties

#整合redis配置
# redis数据源配置
#spirngboot+mybatis连接redis cluster配置
#最大能够保持空闲状态的链接数
redis.maxIdle=2000
#最大连接数
redis.maxTotal=20000
#最大的等待时长 毫秒
redis.maxWaitMillis=20000
#当调用borrow Object方法时,是否进行有效性检查
redis.testOnBorrow=false
#集群节点配置
redis.nodes=192.168.206.41:8001,192.168.206.41:8002,192.168.206.42:8003,192.168.206.42:8004,192.168.206.43:8005,192.168.206.43:8006
#企业中配置会更多   更深入的了解 配置,优化企业中实战使用

2.3 springboot加载自定义配置文件

package com.aaa.springboot.property;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @author:张先生
 * @description:
 * @date 2022/8/12 9:51
 */
@Component//交给IOC容器管理
@PropertySource("classpath:project.properties") //指定自定义配置文件的路径
@ConfigurationProperties(prefix = "redis")//如果自定义配置文件中有多个不同技术配置,指定前缀
@Data
public class RedisProperties {
    //最大能够保持空闲状态的链接数
   private int maxIdle;
   //最大连接数
   private int maxTotal;
   //最大的等待时长 毫秒
   private int maxWaitMillis;
   //当调用borrow Object方法时,是否进行有效性检查
   private boolean testOnBorrow;
   //集群节点配置
   private String nodes;
}

2.4 配置rediscluster-config  使用配置连接配置数据源

使用spring-boot-starter-data-redis中提供的JedisConnectionFactory连接redis集群

package com.aaa.springboot.config;

import com.aaa.springboot.property.RedisProperty;
import com.aaa.springboot.util.RedisCustomCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;

/**
 * @version 1.0
 * @author:张先生(2498841809@qq.com)
 * @description:
 * @date 2022/8/12 14:26
 */
@Configuration //相当于 redis-cluster-config.xml  <beans></beans>
public class RedisClusterConfig {
    //依赖注入
    @Resource
    private RedisProperty redisProperty;

    /**
     * 初始化  JedisConnectionFactory   redis 连接工厂类
     * @return
     */
    @Bean//相当于 <bean id=jedisConnectionFactory class =org.springframework.data.redis.connection.jedis.JedisConnectionFactory>
    public JedisConnectionFactory jedisConnectionFactory(){
        //实例化对象  使用 redis集群 + redis连接池
        //   RedisClusterConfiguration clusterConfig, JedisPoolConfig poolConfig
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(clusterConfig(),poolConfig());
        //返回对象
        return jedisConnectionFactory;
    }

    /**
     * 初始化RedisClusterConfiguration redis 集群配置
     * @return
     */
    @Bean
    public RedisClusterConfiguration clusterConfig(){
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        String nodes = redisProperty.getNodes();
        String[] nodesArray = nodes.split(",");
        for (String hostAndPort : nodesArray) {
            String[] hostAndPortArray = hostAndPort.split(":");
            RedisNode node1 = new RedisNode(hostAndPortArray[0],Integer.valueOf(hostAndPortArray[1]));
            redisClusterConfiguration.addClusterNode(node1);

        }
        return redisClusterConfiguration;
    }



    /**
     * 初始化 JedisPoolConfig 连接池
     * @return
     */
    @Bean
    public JedisPoolConfig poolConfig(){
        //实例化连接池配置  企业实战中可以更多自定义配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(redisProperty.getMaxIdle());
        jedisPoolConfig.setMaxTotal(redisProperty.getMaxTotal());
        jedisPoolConfig.setTestOnBorrow(redisProperty.isTestOnBorrow());
        jedisPoolConfig.setMaxWaitMillis(redisProperty.getMaxWaitMillis());

        return jedisPoolConfig;
    }

    /**
     * 在SpringRedisConfig加载时 执行该方法给RedisCustomCache中jedisConnectionFactory对象赋值
     */
    @Bean
    public void setRedisCustomCacheJCF(){
        RedisCustomCache.setJedisConnectionFactory(jedisConnectionFactory());
    }
}

2.5 编写mybatis自定义缓存类

通过mybatis调用redis集群缓存。 实际使用的mybatis二级缓存,调用第三方缓存。

XML 映射器_MyBatis中文网

MyBatis 内置了一个强大的事务性(一旦进行CUD,缓存就会被清空)查询(只有查询有缓存)缓存机制,它可以非常方便地配置和定制。默认情况下,只启用了本地的会话缓存(只开启了一级缓存,无论想不想用,都是开启),它仅仅对一个会话中的数据进行缓存(一级缓存是基于SqlSession)。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行(二级缓存需要自己开启,二级缓存是语句XxxMapper.xml) <cache>

mybatis二级缓存详解:

1,所有 select 语句的结果将会被缓存  除非自己配置去掉缓存useCache="false"

    2,所有 insert、update 和 delete 语句会刷新缓存。(增删改是改变数据库数据,改变之后一定要重新缓存)除非自己配置不刷新 flushCache="false"

    3,缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。(当1025个热点数据准备缓存时,先把缓存最近最少使用的踢出去)

    默认策略可以通过 eviction="FIFO"  改为先进先出

    4,缓存不会定时进行刷新(也就是说,没有刷新间隔)。如果想定时刷新(定时把所有缓存内容清空)配置flushInterval=""

    5,缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用(一共有多少个被缓存)。

    6,缓存会被视为读/写缓存 (readOnly="false"),这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

     如果配置的是readOnly="false"  每次请求获取缓存数据时,缓存会把当前缓存的数据复制一份返回给调用或者线程,修改复制品,对原来缓存没有任何影响,

     速度慢,但是安全性好。。。。 浪费内存

     如果配置的是readOnly="true"  每一个请求获取缓存数据时,都是同一份数据,如果有请求改了,其他人拿到的也会被修改,速度快,因为没有复制品

     但是安全性不好。。。 节省内存

package com.aaa.springboot.util;

import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

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

/**
 * @version 1.0
 * @author:张先生
 * @description:
 * @date 2022/8/12 15:09
 */
public class RedisCustomCache implements Cache {
    //每个被缓存对象都要生成一个唯一id
    private String id;

    //定义静态方法
    private static JedisConnectionFactory jedisConnectionFactory;

    //缓存读写策略  读写互斥  写读互斥 写写互斥 读读共享
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 无法使用正常注入,使用该对象,所以使用静态方法注入,让里面的jedisConnectionFactory通过该方法赋值
     * @param jedisConnectionFactory
     */
    public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCustomCache.jedisConnectionFactory = jedisConnectionFactory;
    }

    /**
     * 提供一个接受Spring参数最为id的构造器
     * @return id
     */
    public RedisCustomCache(String id) {
        if (null==id){
            throw  new IllegalArgumentException("id不能为空!!!!");
        }
        this.id = id;
    }


    /**
     * 获取缓存对象ID 一定把ID返回
     * @return
     */
    @Override
    public String getId() {
        return this.id;
    }

    /**
     * 向redis集群中写入缓存对象
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        //获取redis连接
        RedisConnection connection = jedisConnectionFactory.getConnection();
        //使用spring整合redis包中的对象对key和value序列化
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        byte[] keySerializeByteArray = jdkSerializationRedisSerializer.serialize(key);
        byte[] valueSerializeByteArray = jdkSerializationRedisSerializer.serialize(value);
        //向redis写入缓存对象
        connection.set(keySerializeByteArray,valueSerializeByteArray);

    }

    /**
     * 通过key获取
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        //获取redis连接
        RedisConnection connection = jedisConnectionFactory.getConnection();
        //使用spring整合redis包中的对象对key和value序列化
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        byte[] keySerializeByteArray = jdkSerializationRedisSerializer.serialize(key);
        //通过序列化key获取序列化的值
        byte[] valueSerializeByteArray = connection.get(keySerializeByteArray);
        //反序列化并返回对象
        return jdkSerializationRedisSerializer.deserialize(valueSerializeByteArray);
    }

    /**
     * 通过key 移除缓存
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        //获取redis连接
        RedisConnection connection = jedisConnectionFactory.getConnection();
        //使用spring整合redis包中的对象对key和value序列化
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        byte[] keySerializeByteArray = jdkSerializationRedisSerializer.serialize(key);
        //建议不要直接删除,因为高并发时,如果每个key都直接删除,会影响redis效率
        //设置key立马过期,后面redis使用不同的淘汰策略去批量删除过期key
        return connection.expireAt(keySerializeByteArray,0);
    }

    /**
     * 清空所有缓存
     */
    @Override
    public void clear() {
        //获取redis连接
        RedisConnection connection = jedisConnectionFactory.getConnection();
        //清空所以库
        //connection.flushDb();
        connection.flushAll();
    }

    /**
     * 获取缓存数量
     * @return
     */
    @Override
    public int getSize() {
        //获取redis连接
        RedisConnection connection = jedisConnectionFactory.getConnection();
        Long aLong = connection.dbSize();
        return Integer.valueOf(aLong.toString());
    }

    /**
     * 缓存读写锁 (缓存读写策略)
     * @return
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
}

 2.6 整合测试

1)注意所有的实体类必须序列化 (只要使用了mybatis二级缓存,就需要这么做)

 2) 启动redis集群,使用客户端命令测试是否正常

使用脚本启动集群:

./shelldir/redis-start-stop.sh

使用客户端命令测试:

/usr/redis/bin/redis-cli -c  -h 192.168.170.31 -p 8001

192.168.170.31:8001> cluster info 发现正常

3) 启动sbm项目,使用swagger做查询 测试

第一请求时,在idea控制台可以看到执行语句,说明是从数据获取

清空控制台,再次请求,发现控制台就不再执行语句,而是看到cache hit Ratio 命中率

 

 请求两次,从缓存中取了一次 1/2

 

 请求3次 2次从缓存中获取 2/3

请求4次, 3次从缓存中获取 3/4

清空缓存,再次请求,再观察控制台

/usr/redis/bin/redis-cli -c  -h 192.168.206.43 -p 8006

192.168.206.43:8006> flushdb

/usr/redis/bin/redis-cli -c  -h 192.168.206.43 -p 8004

192.168.206.43:8004> flushdb

说明清空缓存后,会再次从数据获取

继续刷新请求

增删改后,再次观察控制台(增删改后,一定会自动清空缓存,再次从数据库查询,再次缓存)

先执行添加操作

 

 再执行查询:

 查询方法和CUD方法必须同一个mapper才会CUD后,清空缓存,重新缓存

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值