写点什么

SpringBoot3 集成 Redis

作者:知了一笑
  • 2023-08-15
    浙江
  • 本文字数:5532 字

    阅读完需:约 18 分钟

SpringBoot3集成Redis

标签:Redis.Mybatis.Lock;

一、简介

缓存在项目开发中,基本上是必选组件之一,Redis 作为一个key-value存储系统,具备极高的数据读写效率,并且支持的数据类型比较丰富,在业务场景中的应用非常广泛;


Redis 典型的应用场景就是数据缓存能力,用来解决业务中最容易出现的查询性能问题,提升系统的响应效率;其次就是分布式锁机制,用来解决分布式系统中多线程并发处理资源的安全问题;

二、工程搭建

1、工程结构

2、依赖管理

Redis 的客户端使用的是lettuce组件的6.2.4.RELEASE版本,该组件具备可扩展和线程安全的特性,支持 Redis 各种高级功能,如哨兵,集群,流水线,自动重新连接等;


<!-- Redis组件 --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId>    <version>${spring-boot.version}</version></dependency><!-- Spring集成Redis组件 --><dependency>    <groupId>org.springframework.integration</groupId>    <artifactId>spring-integration-redis</artifactId>    <version>${spring-integration.version}</version></dependency>
复制代码

3、Redis 配置

配置文件,在lettuce组件中默认使用的是common-pool2连接池组件,在配置中定义几个常用的核心参数;


spring:  # Redis配置  data:    redis:      host: 127.0.0.1      port: 6379      database: 1      # 读超时      timeout: 3000      # 连接超时      connectTimeout: 5000      # Lettuce连接池      lettuce:        pool:          # 最小空闲连接          min-idle: 5          # 最大空闲连接          max-idle: 10          # 最大连接数          max-active: 100          # 连接分配应该阻塞的最大时间          max-wait: 2000
复制代码


配置类


@Configurationpublic class RedisConfig {
/** * RedisTemplate模板 */ @Bean("redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); redisTemplate.setKeySerializer(new StringRedisSerializer()); return redisTemplate; }
/** * StringRedisTemplate模板 */ @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(); stringRedisTemplate.setConnectionFactory(factory); stringRedisTemplate.setKeySerializer(new StringRedisSerializer()); return stringRedisTemplate; }
/** * Redis分布式锁 */ @Bean public RedisLockRegistry redisLockRegistry(RedisConnectionFactory factory){ // 1、锁的密钥前缀:REDIS-LOCK // 2、锁的过期时间:20秒 return new RedisLockRegistry(factory, "REDIS-LOCK",20000L); }}
复制代码

三、Redis 用法

1、环境搭建

Redis 文档中推荐使用brew工具安装的方式,本地环境中是4.1版本,可以搜索到redis@6.2,即安装该版本,并且可以使用brew命令管理 Redis 的启动和停止;


brew --versionHomebrew 4.1.0
brew search redis==> Formulaeredis redis-leveldb redis@3.2 redis@4.0 redis@6.2
brew install redis@6.2
brew services start redis@6.2==> Successfully started `redis@6.2` (label: homebrew.mxcl.redis@6.2)
brew services restart redis@6.2==> Successfully stopped `redis@6.2` (label: homebrew.mxcl.redis@6.2)==> Successfully started `redis@6.2` (label: homebrew.mxcl.redis@6.2)
brew services stop redis@6.2==> Successfully stopped `redis@6.2` (label: homebrew.mxcl.redis@6.2)
复制代码


配置 Redis 环境变量,这样可以在命令行使用 Redis 原生的指令进行启动和客户端操作,在测试完成之后,Mac 系统本地的redis@6.2环境就配置好了;


open -e ~/.bash_profile
export REDIS_HOME=/usr/local/opt/redis@6.2export PATH=$REDIS_HOME/bin:$PATH
source ~/.bash_profile
redis-serverRedis 6.2.13 ,Running in standalone mode , Port: 6379 , PID: 7966
redis-cli127.0.0.1:6379> select 1OK127.0.0.1:6379> set redis-key redis-valueOK127.0.0.1:6379> get redis-key"redis-value"127.0.0.1:6379> del redis-key(integer) 1
复制代码

2、数据类型

Strings:字符串是最基本的数据类型,表示一个字节序列,也是最常使用的类型,单个最大能够存储512MB


public void dataString () throws Exception {    // 1、添加K-V缓存    redisTemplate.opsForValue().set("key-01","value-01",300, TimeUnit.SECONDS);    // 2、添加JSON格式    JsonMapper jsonMapper = new JsonMapper() ;    String jsonVar = jsonMapper.writeValueAsString(new IdValue(1,"Json格式")) ;    redisTemplate.opsForValue().set("key-02",jsonVar,500,TimeUnit.SECONDS);    Object key01 = redisTemplate.opsForValue().get("key-01");    Object key02 = redisTemplate.opsForValue().get("key-02");    log.info("key01:{},key02:{}",key01,key02);}
复制代码


Lists:链表结构,可以有序的存储多个字符串值,基于数据左写入右读取的方式可以模拟消息队列,单个最大长度为2^32-1


public void dataList (){    // 1、左侧写数据    redisTemplate.opsForList().leftPushAll("data-list","value-01","value-02","value-03");    // 2、右侧读数据    Object rightPop01 = redisTemplate.opsForList().rightPop("data-list") ;    Object rightPop02 = redisTemplate.opsForList().rightPop("data-list") ;    log.info("rightPop01:{},rightPop02:{}",rightPop01,rightPop02);}
复制代码


Sets:用来存储无序的、唯一的字符串集合,通常用来跟踪分析唯一项的业务场景,比如唯一标签下的用户群,独立 IP 等;


public void dataSet (){    // 1、写数据    redisTemplate.opsForSet().add("data-set","Java","C++","Python","C++");    // 2、读数据    Object pop01 = redisTemplate.opsForSet().pop("data-set");    Object pop02 = redisTemplate.opsForSet().pop("data-set");    log.info("pop01:{},pop02:{}",pop01,pop02);}
复制代码


Hashes:Redis 本身的K-V结构上,在 value 中可以再次使用K-V数据类型,在涉及大规模的关系型数据存储时,会优先使用该类型;


public void dataHash (){    // 1、写数据    HashMap<String,String> hashMap = new HashMap<>() ;    hashMap.put("key1","value1") ;    hashMap.put("key2","value2") ;    redisTemplate.opsForHash().putAll("data-hash",hashMap);    // 2、读数据    Object kv1 = redisTemplate.opsForHash().get("data-hash","key1");    Object kv2 = redisTemplate.opsForHash().get("data-hash","key2");    log.info("kv1:{},kv2:{}",kv1,kv2);}
复制代码


Sorted-sets:唯一的字符串集合,但是可以通过每个字符串的相关分数来维持顺序,这里的分数可以表示排行顺序,也可以标识为对象的权重或优先级;


public void dataSortedSet (){    // 1、写数据    redisTemplate.opsForZSet().add("sorted-set","Java",1.0);    redisTemplate.opsForZSet().add("sorted-set","Python",3.0);    redisTemplate.opsForZSet().add("sorted-set","C++",2.0);    // 2、读数据    Object popMax = redisTemplate.opsForZSet().popMax("sorted-set");    Object popMin = redisTemplate.opsForZSet().popMin("sorted-set");    log.info("popMax:{},popMin:{}",popMax,popMin);}
复制代码

3、加锁机制

使用SETNX方法即代码中的setIfAbsent,如果 key 不存在,则设置 key 保存字符串的值,也就相当于 SET,当 key 已经存在时,不执行任何操作;


@Servicepublic class RedisLockService {    @Resource    private RedisTemplate<String,Object> redisTemplate ;
public Boolean lock (String lock,long timeout){ return redisTemplate.opsForValue().setIfAbsent(lock,lock,timeout, TimeUnit.SECONDS); } public void unLock (String lock){ Object lockVal = redisTemplate.opsForValue().get(lock); if (!Objects.isNull(lockVal) && Objects.equals(lockVal.toString(),lock)){ redisTemplate.delete(lock) ; } }}
复制代码


RedisLockRegistry是由spring-integration组件提供的封装类,基于 Redis 实现分布式锁的能力;有关于两种锁机制的测试,可以查看代码工程中RedisLockTest单元测试类,


@Servicepublic class LockRegistryService {
@Resource protected RedisLockRegistry redisLockRegistry;
/** * 尝试一次加锁 */ @SneakyThrows public <T> Boolean tryLock(T lockKey, Long time) { return redisLockRegistry.obtain(lockKey).tryLock(time, TimeUnit.SECONDS); }
/** * 重试机制多次加锁 */ @SneakyThrows public <T> Boolean reTryLock(T lockKey, Long time,int retryNum) { Boolean lockFlag = tryLock(lockKey, time); if (Boolean.TRUE.equals(lockFlag)) { return Boolean.TRUE; } for (int i = 0; i < retryNum; i++) { if (Boolean.TRUE.equals(tryLock(lockKey, time))){ return Boolean.TRUE; } } return Boolean.FALSE; }
/** * 释放锁 */ public <T> void unlock(T lockKey) { redisLockRegistry.obtain(lockKey).unlock(); }}
复制代码

四、Mybatis 缓存

1、基础配置

在 Mybatis 框架中,一级缓存是指SqlSession级别内;二级缓存是指Mapper级别内,需要在配置文件中开启,二级缓存并不适用于频繁修改数据的业务场景;


配置文件


mybatis-plus:  configuration:    cache-enabled: true    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
复制代码


Mapper 注解,这里使用自定义的MybatisCache类,并且使用 Redis 组件来缓存数据;


@CacheNamespace(implementation = MybatisCache.class)public interface ArticleMapper extends BaseMapper<Article> {
}
复制代码

2、自定义实现

通过实现Cache接口,来定义二级缓存的处理策略,可以运行MybatisTest测试类中的方法,分析其执行的原理,符合Cache-Aside模式,即先查询缓存,如果未命中就查询数据库,并将结果写入缓存,数据更新时会删除相关缓存;


public class MybatisCache implements Cache {
private static final Logger log = LoggerFactory.getLogger(MybatisCache.class);
private RedisTemplate<String,Object> redisTemplate ;
private String id ;
public MybatisCache (String id){ log.info("mybatis...cache...id:{}",id); this.id = id ; }
public RedisTemplate<String,Object> getRedisTemplate(){ if(redisTemplate == null) { synchronized (MybatisCache.class) { if(redisTemplate == null) { redisTemplate = SpringContextUtil.getBean("redisTemplate",RedisTemplate.class); return redisTemplate; } return redisTemplate; } } return redisTemplate ; } @Override public String getId() { return this.id ; }
@Override public void putObject(Object key, Object value) { log.info("mybatis...cache...put...key:{},value:{}",key,value); this.getRedisTemplate().opsForValue().set(String.valueOf(key),value,5, TimeUnit.MINUTES); }
@Override public Object getObject(Object key) { log.info("mybatis...cache...get...key:{}",key); return this.getRedisTemplate().opsForValue().get(String.valueOf(key)) ; }
@Override public Object removeObject(Object key) { log.info("mybatis...cache...remove...key:{}",key); return this.getRedisTemplate().delete(String.valueOf(key)); }
@Override public void clear() { Set<String> keys = this.getRedisTemplate().keys("*" + id + "*"); if (keys != null && keys.size()>0){ log.info("mybatis...cache...clear...keys:{}",keys); this.getRedisTemplate().delete(keys) ; } }
@Override public int getSize() { Set<String> keys = this.getRedisTemplate().keys("*" + id + "*"); if (keys != null){ log.info("mybatis...cache...size...keys:{}",keys.size()); return keys.size() ; } return 0; }}
复制代码

五、参考源码

文档仓库:https://gitee.com/cicadasmile/butte-java-note
源码仓库:https://gitee.com/cicadasmile/butte-spring-parent
复制代码


发布于: 刚刚阅读数: 7
用户头像

知了一笑

关注

公众号:知了一笑 2020-04-08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
SpringBoot3集成Redis_Java_知了一笑_InfoQ写作社区