标签: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

配置类

@Configuration
public 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 --version
Homebrew 4.1.0 brew search redis
==> Formulae
redis 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.2
export PATH=$REDIS_HOME/bin:$PATH source ~/.bash_profile redis-server
Redis 6.2.13 ,Running in standalone mode , Port: 6379 , PID: 7966 redis-cli
127.0.0.1:6379> select 1
OK
127.0.0.1:6379> set redis-key redis-value
OK
127.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已经存在时,不执行任何操作;

@Service
public 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单元测试类,

@Service
public 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

SpringBoot3集成Redis的更多相关文章

  1. 【springBoot】springBoot集成redis的key,value序列化的相关问题

    使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...

  2. SpringBoot集成redis的key,value序列化的相关问题

    使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...

  3. spring集成redis

    redis是一种非关系型数据库,与mongoDB不同的是redis是内存数据库,所以访问速度很快.常用作缓存和发布-订阅式的消息队列.redis官方没有提供windows版本的软件.windows版本 ...

  4. springboot集成redis(mybatis、分布式session)

    安装Redis请参考:<CentOS快速安装Redis> 一.springboot集成redis并实现DB与缓存同步 1.添加redis及数据库相关依赖(pom.xml) <depe ...

  5. Windows环境下springboot集成redis的安装与使用

    一,redis安装 首先我们需要下载Windows版本的redis压缩包地址如下: https://github.com/MicrosoftArchive/redis/releases 连接打开后如下 ...

  6. Springboot集成Redis步骤

    Spring boot 集成Redis的步骤如下: 1.在pom.xml中配置相关的jar依赖: <!--加载spring boot redis包 --> <dependency&g ...

  7. spring boot集成redis的血泪史

    首先说明环境不是我搭建的,然后因项目需要添加redis的时候,麻烦来了.springboot 用的是1.5.9因为以前弄过redis,所以直接拿过来,麻烦了首先是莫名的错误,连项目都启动不了.但是最后 ...

  8. Spring Boot 项目实战(四)集成 Redis

    一.前言 上篇介绍了接口文档工具 Swagger 及项目监控工具 JavaMelody 的集成过程,使项目更加健壮.在 JAVA Web 项目某些场景中,我们需要用缓存解决如热点数据访问的性能问题,业 ...

  9. SpringBoot集成Redis

    1.引入 spring-boot-starter-redis <dependency> <groupId>redis.clients</groupId> <a ...

  10. Springboot 2.0 - 集成redis

    序 最近在入门SpringBoot,然后在感慨 SpringBoot较于Spring真的方便多时,顺便记录下自己在集成redis时的一些想法. 1.从springboot官网查看redis的依赖包 & ...

随机推荐

  1. 2022-09-09:给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数 。 示例 1: 输入: n = 5 输出: 2 解释: 5 = 2 + 3,共有两组连续整数([5],[2,

    2022-09-09:给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数 . 示例 1: 输入: n = 5 输出: 2 解释: 5 = 2 + 3,共有两组连续整数([5],[2,3 ...

  2. 2021-05-05:一个数组中只有两种字符‘G‘和‘B‘,可以让所有的G都放在左侧,所有的B都放在右侧。或者可以让所有的G都放在右侧,所有的B都放在左侧。但是只能在相邻字符之间进行交换操作。返回至少

    2021-05-05:一个数组中只有两种字符'G'和'B',可以让所有的G都放在左侧,所有的B都放在右侧.或者可以让所有的G都放在右侧,所有的B都放在左侧.但是只能在相邻字符之间进行交换操作.返回至少 ...

  3. Django4全栈进阶之路16 项目实战(用户管理):user_list.html用户列表画面设计

    首先在template模板文件夹中新建account子文件夹,用于存放用户管理相关模块页面. 下面开始正式的设计: 1.模块代码设计 {% extends 'base.html' %} {% bloc ...

  4. Django-2:创建项目Project

    命令:django-admin startproject mysite PS C:\Users\liujun> cd e:\pyapp\cmdbPS E:\pyapp\cmdb> djan ...

  5. for循环原理补充、生成器对象、yield冷门用法、生成器表达式的面试题、常见内置函数

    目录 一.for循环原理补充 二.生成器对象 (1).自定义生成器对标range功能(一个参数 两个参数 三个参数 迭代器对象) 三.yield冷门用法 (1).yield与return的对比 四.生 ...

  6. SpringBoot定义优雅全局统一Restful API 响应框架五

    闲话不多说,继续优化 全局统一Restful API 响应框架 做到项目通用 接口可扩展. 如果没有看前面几篇文章请先看前面几篇 SpringBoot定义优雅全局统一Restful API 响应框架 ...

  7. 信息收集_网络扫描_nmap

    信息收集_网络扫描nmap 目标说明 -iL <inputname> (从列表或文件输入) -iR <hostnum> (随机选择生成目标数量) --exclude <h ...

  8. 5个 Istio 访问外部服务流量控制最常用的例子,你知道几个?

    5 个 Istio 访问外部服务的流量控制常用例子,强烈建议收藏起来,以备不时之需. 环境准备 部署 sleep 服务,作为发送请求的测试源: kubectl apply -f samples/sle ...

  9. 2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j。

    2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j. ...

  10. 行行AI人才直播第3期:浙江大学周鑫博士《空中机器人复杂环境高效自主导航—从单机到集群》

    行行AI人才是博客园和顺顺智慧共同运营的AI行业人才全生命周期服务平台. 空中飞行的无人车.无人机集群是科幻电影的常客,也往往是未来科技的一大代表.在电影<星球大战:西斯的复仇>(2005 ...