当前位置: 首页 > news >正文

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录

  • 前言
  • 一、食用步骤
    • 1.1 安装步骤
      • 1.1.1 客户端安装
      • 1.2 添加依赖
      • 1.3 修改配置
      • 1.4 项目使用
      • 1.5 序列化
  • 二、应用场景
    • 2.1 缓存
    • 2.2.分布式锁
      • 2.2.1 redis实现
      • 2.2.2 使用Redisson 作为分布式锁
    • 2.3 全局ID、计数器、限流
    • 2.4 购物车
    • 2.5 消息队列 (List)
    • 2.6 点赞、签到、打卡 (Set)
    • 2.7 筛选(Set)
    • 2.8 排行榜


前言

在日常的Java开发中,Redis是使用频次非常高的一个nosql数据库,数据以key-value键值对的形式存储在内存中,可以做缓存,分布式锁等。

spring-data-redis属于spring-data,提供给spring项目对于redis的操作,里边主要封装了jedis和lettuce两个客户端。


一、食用步骤

1.1 安装步骤

1.1.1 客户端安装

https://redis.io/docs/getting-started/installation/

1.2 添加依赖

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

默认使用lettuce客户端
在这里插入图片描述

1.3 修改配置

spring:redis:host: localhostport: 6379password: database: 0

redis客户端配置连接池

<!-- redis依赖commons-pool 这个依赖一定要添加 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

1.4 项目使用

通过spring-data-redis中为我们提供的 RedisTemplate 这个类操作redis服务器

@RestController
public class RedisController {@Autowiredprivate RedisTemplate redisTemplate;@GetMapping("save")public void save(String key, String value){redisTemplate.opsForValue().set(key, value);}
}

1.5 序列化

运行后,发现redis里的值不直观,不利于排查问题
在这里插入图片描述
原因是RedisTemplate默认的序列化方式导致的,需要重新配置序列化方式

经常使用的对象序列化方式是: Jackson2JsonRedisSerializer

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Bean(name = "redisTemplate")public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(factory);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key的序列化类型redisTemplate.setKeySerializer(stringRedisSerializer); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);// value的序列化类型redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}
}

在这里插入图片描述

二、应用场景

2.1 缓存

  1. String字符串类型: 动态变长字符串
  2. List列表类型:类似LinkedList前后插入和删除速度快
  3. Hash:类似Hashmap数组+链表的数据结构
  4. Set集合类型:类似HashSet,键值是无序唯一
  5. Zset有序集合:Set和Map的结合体,既能保证key唯一、又能根据value做排序

2.2.分布式锁

2.2.1 redis实现

在分布式环境下我们需要保证某一方法同一时刻只能被一个线程执行,或者多个服务的定时任务只能执行一次。

原理:SETNX

redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"

NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。

分布式锁具备的特征

  1. 互斥:任意时刻只能有一个客户端持有锁
  2. 锁超时释放:锁超时要释放,防止资源浪费和死锁
  3. 可重入性:一个线程如果获取了锁之后,可以再次对其请求加锁。(锁续期)
  4. 高性能和高可用:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。
  5. 安全性:锁只能被持有的客户端删除,不能被其他客户端删除

场景伪代码:

//获取锁
ifSETNX key "lock" == 1{ //设置过期时间expire(key,100);try {业务;}catch(){}finally {//释放锁del(key);}
}

以上代码,有以下问题:

  1. setnx和expire不是原子操作,会导致key永久存在
  2. 当线程a锁过期释放了,业务还没执行完,b抢到锁执行方法,此时线程a方法执行完后,把b抢到的锁给释放了…达不到锁的标准

代码如下(示例):

public String testLock() {//uuid保证只能释放当前线程的锁String uuid = UUID.randomUUID().toString();//推荐使用setIfAbsent这样redis在set的时候是单线程的,原子的,不会存在短时间重复set的问题。Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);if (Boolean.TRUE.equals(lock)) {System.out.println("获取分布式锁成功...");try {System.out.println("加锁成功...执行业务");} finally {// lua 脚本解锁String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";// 删除redis.call('get', "lock") 的值和uuid相等的锁,成功返回1 失败返回 0redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock"), uuid);}return "执行完成";} return "获取分布式锁失败";}

2.2.2 使用Redisson 作为分布式锁

官方文档:https://github.com/redisson/redisson/wiki

引入依赖

 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.11.1</version>
</dependency>

配置redission

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyRedissonConfig {//所有对 Redisson 的使用都是通过 RedissonClient@Bean(destroyMethod = "shutdown")public RedissonClient redisson() {// 1、创建配置Config config = new Config();// 路径要加上 redis:// 或者 rediss:// //单节点模式config.useSingleServer().setAddress("redis://192.168.3.25:6379");// 2、根据 Config 创建出 RedissonClient 实例return Redisson.create(config);}
}

使用

@Autowired
RedissonClient redisson;public List<User> getUser() {// 1. 获取一把锁RLock lock = redisson.getLock("lock");// 2. 加锁, 阻塞式等待 默认30s 会自动续期// 指定过期时间就不会自动续期lock.lock();try {Thread.sleep(10000);System.out.println("加锁成功,执行业务...");} catch (Exception e) {System.out.println("报错啦...");} finally {// 3. 解锁 假设解锁代码没有运行,Redisson也不会出现死锁lock.unlock();}return userServiceImpl.queryAll();
}

特点

  1. redission继承了juc的锁
  2. 调用lock方法后,会调用renewExpiration() 开启后台线程『看门狗』,30 / 3 = 10s后自动续期到30s。
  3. 锁的自动续期,如果业务耗时长,运行期间自动给锁续期 ,所以不用担心业务时间过长,锁自动过期被删掉;
//读写锁 锁粒度细,运行越快
RReadWriteLock readWriteLock = redisson.getReadWriteLock("lock");
//读锁 非阻塞
RLock rLock = readWriteLock.readLock();
rLock.lock();
rLock.unlock();
//写锁 阻塞,数据被写锁占有,读锁会被阻塞
RLock wLock = readWriteLock.writeLock();
wLock.lock();
wLock.unlock();

2.3 全局ID、计数器、限流

利用incrby的原子性

  • 计数器:阅读量、点赞数
  • 限流:访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false
Long incr = redisTemplate.opsForValue().increment("incrlock");

2.4 购物车

public void testStringCart() {//添加购物车 +1Car.CartItem cartItem = new Car.CartItem(1L, "iphone13 256G 蓝色",new BigDecimal(100), 1);Car carSave = new Car(Collections.singletonList(cartItem));redisTemplate.opsForValue().set("user_001", JSONObject.toJSONString(carSave));//删除 -1 skuId = 1Car car = JSON.parseObject((String) redisTemplate.opsForValue().get("user_001"), Car.class);//记得判空car.getItems().forEach(i -> {if(Objects.equals(i.getSkuId(), 1L)){i.setCount(i.getCount() - 1);}} );//删除后保存新的购物车redisTemplate.opsForValue().set("user_001", JSONObject.toJSONString(car));
}

2.5 消息队列 (List)

//左推
redisTemplate.opsForList().leftPushAll("list", "西瓜1","西瓜2","西瓜3");
//右取 西瓜1 =》 "西瓜2" =》 "西瓜3" 
redisTemplate.opsForList().rightPop("list");

2.6 点赞、签到、打卡 (Set)

//user_001给新闻01点赞
redisTemplate.opsForSet().add("like:news01","user_001");
redisTemplate.opsForSet().add("like:news01","user_001");
redisTemplate.opsForSet().add("like:news01","user_002");
//set大小为2
redisTemplate.opsForSet().size("like:news01");
//点赞的所有用户 [user_002, user_001]
redisTemplate.opsForSet().members("like:news01");

2.7 筛选(Set)

  • (用户关注)我关注的人也关注了他
  • 可能认识的人
  • 商品筛选
//user1给新闻点赞
redisTemplate.opsForSet().add("like:news01","user_001");
redisTemplate.opsForSet().add("like:news01","user_002");
redisTemplate.opsForSet().add("like:news02","user_001");
redisTemplate.opsForSet().add("like:news02","user_003");
//获取新闻点赞的用户差集 => [user_002]
System.out.println(redisTemplate.opsForSet().difference("like:news01", "like:news02"));
//获取新闻点赞的用户交集 => [user_001]
System.out.println(redisTemplate.opsForSet().intersect("like:news01", "like:news02"));
//获取新闻点赞的用户并集 => [user_002, user_001, user_003]
System.out.println(redisTemplate.opsForSet().union("like:news01", "like:news02"));

2.8 排行榜

点击率排行榜

示例

public void testZset() {//用户点击新闻自增1Double news_01 = redisTemplate.opsForZSet().incrementScore("hot:20230222", "news_01", 1);//初始化ZSetredisTemplate.opsForZSet().add("hot:20230222","news_01", 700);redisTemplate.opsForZSet().add("hot:20230222","news_02", 1700);redisTemplate.opsForZSet().add("hot:20230222","news_03", 3700);redisTemplate.opsForZSet().add("hot:20230222","news_04", 500);//获取新闻点击最多的3条//[DefaultTypedTuple [score=3700.0, value=news_03],// DefaultTypedTuple [score=1700.0, value=news_02], // DefaultTypedTuple [score=700.0, value=news_01]]//reverseRangeWithScores 分数倒序从下标0取到2 System.out.println(redisTemplate.opsForZSet().reverseRangeWithScores("hot:20230222", 0,2));
}

相关文章:

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录前言一、食用步骤1.1 安装步骤1.1.1 客户端安装1.2 添加依赖1.3 修改配置1.4 项目使用1.5 序列化二、应用场景2.1 缓存2.2.分布式锁2.2.1 redis实现2.2.2 使用Redisson 作为分布式锁2.3 全局ID、计数器、限流2.4 购物车2.5 消息队列 (List)2.6 点赞、签到、打卡 (Set)2.7 筛…...

Flutter基础语法(六)var、final、const、late

Flutter基础 第六章 Flutter关键字var、final、const、late的区别与使用 文章目录Flutter基础前言一、var1.var是什么?2.var如何使用3.var自动推断类型4.var可以再次赋值5.var指定类型二、final1.final是什么?2.final声明但不赋值3.final赋值多次4.final正常使用三、const1.…...

Linux之安装node

Linux之安装node步骤如下 1.去网站下载node 下载地址&#xff1a; https://npm.taobao.org/mirrors/ 2.上传到指定目录下 3.解压 tar -zxvf node-v17.3.0-linux-x644.配置node环境变量 //执行以下命令 vim /etc/profile //在path中加入以下内容 /usr/local/node-v15.14.0/b…...

二叉树、二叉搜索树、二叉树的最近祖先、二叉树的层序遍历【零神基础精讲】

来源0x3f&#xff1a;https://space.bilibili.com/206214 文章目录二叉树[104. 二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree/)[111. 二叉树的最小深度](https://leetcode.cn/problems/minimum-depth-of-binary-tree/)[129. 求根节点到叶节点…...

【算法】【数组与矩阵模块】求最长可整合子数组和子数组的长度

目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过&#xff0c;但是不保证100%的测试用例&#xff0c;如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识&#xff01; 问题介绍 …...

数据结构:循环队列的实现(leetcode622.设计循环队列)

目录 一.循环队列简单介绍 二.用静态数组实现循环队列 1.数组循环队列结构设计 2.数组循环队列的堆区内存申请接口 3.数据出队和入队的接口实现 4.其他操作接口 5.数组循环队列的实现代码总览 三.静态单向循环链表实现循环队列 1.链表循环队列的结构设计 2.创建静…...

[qiankun]实战问题汇总

[qiankun]实战问题汇总ERROR SyntaxError: Cannot use import statement outside a module问题分析解决方案子应用命名问题问题分析解决方案jsonpFunction详细错误信息问题分析解决方案微应用的注册问题Uncaught Error: application cli5-beta6-test-name died in status LOADI…...

Kafka(6):服务端常用参数配置

参数配置&#xff1a;config/server.properties # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership.…...

2023爱分析·云原生智能运维中台市场厂商评估报告:秒云(miaoyun.io)

目录 1. 研究范围定义 2. 云原生智能运维中台市场定义 3. 厂商评估&#xff1a;秒云&#xff08;miaoyun.io&#xff09; 4. 入选证书 1. 研究范围定义 数字化时代&#xff0c;应用成为企业开展各项业务的落脚点。随着业务的快速发展&#xff0c;应用的功能迭代变得越…...

hadoop容器化部署

1、原容器 java:openjdk-8u111-jre jre路径&#xff1a; /usr/lib/jvm/java-8-openjdk-amd64 /usr/lib/jvm/java-1.8.0-openjdk-amd64 2、安装ssh docker run -it --name hadoop-test java:openjdk-8u111-jre bash apt-get update apt-get install openssh service ssh start …...

【07-JVM面试专题-JVM运行时数据区的虚拟机栈你知道吗?它的基本结构是什么呢?你知道栈帧的结构吗?那你说说动态链接吧?】

JVM运行时数据区的虚拟机栈你知道吗&#xff1f;它的基本结构是什么呢&#xff1f;你知道栈帧的结构吗&#xff1f;那你说说动态链接吧&#xff1f; JVM运行时数据区的虚拟机栈你知道吗&#xff1f;它的基本结构是什么呢&#xff1f;你知道栈帧的结构吗&#xff1f;那你说说动态…...

Java性能优化-GC优化基础

GC优化基础 调整堆大小 如果在FULL GC系统进行了交换&#xff0c;停顿时间会增长几个数量级&#xff0c;OS 如果G1 GC和后台进程处理堆&#xff0c;将会出现等待数据从磁盘复制到主内存时间较长&#xff0c;速度和下降并且并发模式可能失效 linux 关闭交换区 swapoff -a linu…...

【Tomcat】IDEA编译Tomcat源码-手把手教程

一、环境准备Tomcat不同版本之间有一定的兼容性问题~如下图所示&#xff1a;官网地址&#xff1a;https://tomcat.apache.org/whichversion.html下载tomcat9官网上面的源码&#xff1a;这一篇文章主要是带着大家在自己的IDEA跑起来一个Tomcat。使用的版本是Tomcat9.0.55 和 JDK…...

如何弄小程序?公司企业可以这样做小程序

公司企业现在对于小程序的需求已经是刚需了&#xff0c;即使已经有官网的情况下&#xff0c;也会考虑再弄一个小程序来做小程序官网。那么公司企业如何弄小程序呢&#xff1f;下面跟大家说说方法。 流程一、找小程序服务商 由于一些公司企业并不像现在的互联网公司企业那样有…...

【Git】IDEA集合Git和码云

目录 7、IDEA集合Git 7.1 配置Git忽略文件-IDEA特定文件 7.2 定位 Git 程序 7.3 初始化本地库 7.4 添加到暂存区 7.5 提交到本地库 7.6 切换版本 7.7 创建分支 7.8 切换分支 7.9 合并分支 7.10 解决冲突 8、 Idea集成码云 8.1 IDEA 安装码云插件 8.2 分析工程到码…...

[USACO03FALL / HAOI2006] 受欢迎的牛 G(C++,强连通分量)

题目背景 本题测试数据已修复。 题目描述 每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂&#xff0c;每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 AAA 喜欢 BBB&#xff0c;BBB 喜欢 CCC&#xff0c;那么…...

Vue 动态路由接口数据结构化为符合VueRouter的声明结构及菜单导航结构、动态路由懒加载方法

Vue 动态路由接口数据结构化为符合VueRouter的声明结构及菜单导航结构、动态路由懒加载方法 实现目标 项目打包代码实现按需分割路由懒加载按需打包&#xff0c;排除引入子组件的冗余打包&#xff08;仅处理打包冗余现象&#xff0c;不影响生产部署&#xff09;解决路由懒加载…...

Python----------字符串

1.转义字符 注&#xff1a;转义字符放在你所想效果字符前 2.原始字符串 print(r"D:\three\two\one\now") ->D:\three\two\one\now注&#xff1a; 在使用原始字符串时&#xff0c;转义字符不再有效&#xff0c;只能当作原始的字符&#xff0c;每个字符都没有特殊…...

日志收集笔记(架构设计、Log4j2项目初始化、Lombok)

1 架构设计 ELK 技术栈架构设计图&#xff1a; 从左往右看&#xff0c; Beats&#xff1a;主要是使用 Filebeat&#xff0c;用于收集日志&#xff0c;将收集后的日志数据发送给 Kafka&#xff0c;充当 Kafka 的生产者Kafka&#xff1a;高性能消息队列&#xff0c;主要起缓冲…...

一文教你玩转 Apache Doris 分区分桶新功能|新版本揭秘

数据分片&#xff08;Sharding&#xff09;是分布式数据库分而治之 (Divide And Conquer) 这一设计思想的体现。过去的单机数据库在大数据量下往往面临存储和 IO 的限制&#xff0c;而分布式数据库则通过数据划分的规则&#xff0c;将数据打散分布至不同的机器或节点上&#xf…...

数据挖掘,计算机网络、操作系统刷题笔记54

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记54 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…...

将数组中的每个元素四舍五入到指定的精度numpy.rint()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 将数组中的每个元素 四舍五入到指定的精度 numpy.rint() 选择题 请问np.rint(a)的输出结果是? import numpy as np anp.array([-1.72,-1.3,0.37,2.4]) print("【显示】a&#xff1a;\n…...

Web安全之服务器端请求伪造(SSRF)类漏洞详解及预防

如何理解服务器端请求伪造&#xff08;SSRF&#xff09;类漏洞当服务器向用户提交的未被严格校验的URL发起请求的时候&#xff0c;就有可能会发生服务器端请求伪造&#xff08;SSRF&#xff0c;即Server-Side Request Forgery&#xff09;攻击。SSRF是由攻击者构造恶意请求URL&…...

LeetCode:239. 滑动窗口最大值

239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-…...

JS 函数参数(动态参数、剩余参数)

需求&#xff1a;求和函数 传入不同实参 求和出来1.动态参数 arguments 只存在于函数内function getSum() {//arguments 获取传递的所有参数 是一个伪数组let num 0for(let i0;i<arguments.length;i){num arguments[i]}return num}//调用console.log(getSum(1,2,3))consol…...

365天深度学习训练营-第J3周:DenseNet算法实战与解析

目录 一、前言 二、论文解读 1、DenseNet的优势 2、设计理念 3、网络结构 4、与其他算法进行对比 三、代码复现 1、使用Pytorch实现DenseNet 2、使用Tensorflow实现DenseNet网络 四、分析总结 一、前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习…...

Parisland NFT 作品集

该作品集用来自 Parisland 体验&#xff0c;共包含 11 个 NFT 资产&#xff0c;把你的土地装扮成一个眼花缭乱的热带天堂吧&#xff01; 登上芭黎丝的爱情船和戴上豪华的螺旋爱情戒指&#xff0c;成为她在数位世界举办的真人秀的一部分吧&#xff01;该系列还包含两个传奇级别的…...

uniapp: 基础开发官网文档

1、uniapp官网文档&#xff1a;https://uniapp.dcloud.net.cn/component/2、uView跨端UI组件库&#xff1a;http://v1.uviewui.com/components/intro.html3、lunch-request&#xff08;类似axios的请求库&#xff09;&#xff1a;https://www.quanzhan.co/luch-request/handboo…...

mybatis中配置连接池的原理介绍分析

1.连接池&#xff1a;我们在实际开发中都会使用连接池。因为它可以减少我们获取连接所消耗的时间。2、mybatis中的连接池mybatis连接池提供了3种方式的配置&#xff1a;配置的位置&#xff1a;主配置文件SqlMapConfig.xml中的dataSource标签&#xff0c;type属性就是表示采用何…...

二叉树——路径总和

路径总和 链接 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点…...