SpringBoot3集成Redis

news2024/11/22 16:13:02

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/880029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【图像分类】理论篇 (4)图像增强opencv实现

随机旋转 随机旋转是一种图像增强技术&#xff0c;它通过将图像以随机角度进行旋转来增加数据的多样性&#xff0c;从而帮助改善模型的鲁棒性和泛化能力。这在训练深度学习模型时尤其有用&#xff0c;可以使模型更好地适应各种角度的输入。 原图像&#xff1a; 旋转后的图像&…

海外应用ASO优化的小技巧1

根据数据显示&#xff0c;用户需要7秒的时间来决定是否下载应用程序&#xff0c;所以应用的外观是用户决策的一个关键方面。 1、了解用户 63%的应用是通过应用商店搜索找到的&#xff0c;这是查找新应用最常见的方法。然而它也表明个人正在寻找特定的程序。了解用户想要做什么…

Qt 加载 libjpeg 库出现“长跳转已经运行”错误

在我以为升级到 Qt5.15.9 后&#xff0c;运行没有什么问题时&#xff0c;问题就来了 在加载 jpeg 格式的图片时&#xff0c;出现了“长跳转已经运行”的错误 这个错误一般是由 setjmp/longjmp 函数触发的&#xff0c;出现的可能的原因有以下几种&#xff08;听听 chatgpt 的回…

存款多少可以不用上班?这组数据告诉你

手有余粮&#xff0c;心中不慌&#xff0c;兜里有钱才能大胆走四方。 最近&#xff0c;与“存款”相关的词条频繁登上热搜榜&#xff0c;比如#普通人存1万块需要多久#、 #你用多久攒到人生第一个10万元#、#月薪5000两年存了8万#、#超7成年轻人存款不足10万#等词条。 △ 截图来…

MySQL 45讲笔记(1-10讲)

1. SQL语句如何开始执行&#xff1f; MySQL分为Server和存储引擎两部分&#xff1a; Server层包含连接器、存储缓存、分析器、执行器等&#xff0c;以及所有的内置函数&#xff08;事件、日期&#xff09;等等&#xff0c;还有视图、触发器。 存储引擎是负责数据的存储和提取&a…

C语言暑假刷题冲刺篇——day1

目录 一、选择题 二、编程题 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C语言每日一练 ✨其他专栏&#xff1a;代码小游戏C语言初阶&#x1f91d;希望作者的文章能对你…

airflow的安装

文章目录 前言一、Airflow基本概念1.概念2. 名词 二、Airflow安装1.Airflow官网2.安装python环境3. 安装anaconda34. 创建airflow虚拟环境5. 安装Airflow1. 更改pip源2. 安装airflow3. 初始化airflow4. 查看版本5. 查看airflow安装的路径6.创建账号7. 启动airflow调度8. 启动ai…

解决hbase节点已下线,但在status中显示为dead问题

工作中需要下线4台hbase小节点&#xff0c;下线完成后使用status 命令查看,有一台为dead状态: 使用status detailed 查看&#xff0c;发现“hd-03"这台节点是dead。 检查各节点配置文件无误&#xff0c;并使用 /opt/hbase/bin/hbase-daemon.sh restart master 重启两个…

改良版自编小程序上线啦---助力孟德尔随机化(Mendelian Randomization)数据挖掘

孟德尔随机化步骤相对简单固定&#xff0c;一共就是3步&#xff0c;但是如果我们一个一个的对研究变量和结果数据进行筛选&#xff0c;也是挺费时间的&#xff0c;在上篇文章《自编R语言小程序助力孟德尔随机化(Mendelian Randomization)数据挖掘》中&#xff0c;咱们自编了一个…

【ppt密码】忘记了PPT的密码,怎么办?

PPT文件设置了保护密码&#xff0c;但是密码忘记了&#xff0c;无法打开PPT文件、无法编辑PPT文件了该怎么办&#xff1f;PPT文件的两种保护密码该如何解密&#xff1f; 首先是打开密码 网上有一种解决方法&#xff1a; 1、重新命名PPT文件&#xff0c;将其后缀改为zip格式&…

字节跳动在PGO反馈优化技术上的探索与实践

背景 随着字节跳动的业务迅速增长&#xff0c;微服务的性能优化工作显得尤为重要&#xff0c;对于头部应用来说&#xff0c;提升若干百分点的性能也能为公司节省巨大的服务器资源成本。 编译器优化是软件性能优化的一种常用方法&#xff0c;相比其它特定的性能优化方法&#…

从零开始 Spring Cloud 11:Elasticsearch II

从零开始 Spring Cloud 11&#xff1a;Elasticsearch II 图源&#xff1a;laiketui.com 在上篇文章中我们学习了 es 的基本功能&#xff0c;在本篇文章中会学习 es 的一些高级功能&#xff0c;比如&#xff1a; 聚合查询自动补全集群部署 数据聚合 类型 **聚合&#xff08…

toB 业务分析

1、 如何透彻分析B端客户的需求&#xff1f; - 知乎我在讲《如何分析客户需求》这门课时&#xff0c;经常会问学员&#xff1a;“开发客户的最大困难是什么&#xff1f;”有人说价格高不好卖&#xff0c;有人说客户需求不好把握&#xff0c;有人说客户地处偏远&#xff0c;素养…

部署WMS仓储管理系统前要知道哪些关键点

随着物流行业的快速发展&#xff0c;WMS仓储管理系统解决方案已成为企业仓储管理的重要工具。然而&#xff0c;在部署WMS仓储管理系统之前&#xff0c;企业需要了解一些关键点&#xff0c;以确保系统的成功实施和高效运行。本文将介绍部署WMS前需要知道的关键点。 1、明确目标与…

一个小时入门 EJB

前置知识 在开始学习Java EE的Enterprise JavaBeans (EJB)之前&#xff0c;以下是一些你可能需要提前了解的技术和概念&#xff1a; Java基础&#xff1a;熟悉Java的基础知识&#xff0c;包括面向对象的概念&#xff08;例如类、接口、继承和多态等&#xff09;、基本的数据结…

【从零学习python 】27. Python 函数的使用及嵌套调用

文章目录 函数的文档说明1. 基本使用2. 高级使用 函数应用&#xff1a;打印图形和数学计算目标思考&实现1参考代码1 思考&实现2参考代码2 函数的嵌套调用进阶案例 函数的文档说明 1. 基本使用 def test(a, b):"用来完成对2个数求和" # 函数第一行写一个字…

产品需求管理软件:了解常见选择和功能

产品需求管理软件是一种非常重要的工具。它可以帮助企业更好地理解客户需求&#xff0c;提高产品开发效率并降低成本。本文将介绍一些常见的产品需求管理软件及其主要功能。 “产品需求管理软件有哪些&#xff1f;比较流行的有Zoho Projects、Trello、Asana、Smartsheet等。” …

「并发编程 」

一、 为什么会有线程安全问题 1&#xff09;程序和CPU间的协作关系 CPU组成 寄存器 存储了从内存加载的数据&#xff08;从内存中将数据加载到 L1&#xff0c;L2&#xff0c;L3 缓存&#xff0c;再到寄存器&#xff09; &#xff1b;寄存器的运行速度比内存快好多个级别&…

哈工大开源“活字”对话大模型

一、介绍 大规模语言模型&#xff08;LLM&#xff09;在自然语言处理的通用领域已取得了令人瞩目的成功。对于广泛的应用场景&#xff0c;这种技术展示了强大的潜力&#xff0c;学术界和工业界的兴趣也持续升温。哈工大自然语言处理研究所30余位老师和学生参与开发了通用对话大…

【BUG】docker安装nacos,浏览器却无法访问到页面

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…