redis的使用场景

1. redis的使用场景

redis使用场景的案例:[1]热点数据的缓存[2]分布式锁[3]短信业务(登录注册时)

2. redis实现注册登录功能

在这里插入图片描述

代码

在发送验证码时,先判断数据库是否有该手机号,有则发送验证码(此时redis缓存中有发送过该验证码,则返回已发送,防止多次发送验证码)并储存到redis中(手机号作为唯一的在加上过期时间)。

在登录验证手机号和验证码时,根据输入的手机号(唯一key在redis中查询)是否跟输入的验证码(不为空)时相同,登录成功,并删除该redis数据(验证码只能作为一次登录)。

@Autowiredprivate StringRedisTemplate redisTemplate;@GetMapping("send")public R send(String phone) throws Exception {//1. 校验手机号是否存在---连接数据库if(phone.equals("15137437506")||phone.equals("15959715454")){if(redisTemplate.hasKey("code::"+phone)){return new R(500,"验证码已发送",null);}//2. 发生验证码String code = SendMsgUtil.sendCode(phone);//3. 保存验证码到redis.redisTemplate.opsForValue().set("code::"+phone,code,5, TimeUnit.MICROSECONDS);return new R(200,"发送成功",null);}return new R(500,"手机号未注册",null);}@PostMapping("login")public R login(@RequestBody LoginVo loginVo){//1. 校验验证码String code = redisTemplate.opsForValue().get("code::" + loginVo.getPhone());String phone = loginVo.getPhone();if(StringUtils.hasText(loginVo.getCode()) && loginVo.getCode().equals(code)){if(phone.equals("18839986970")||phone.equals("15137437506")){redisTemplate.delete("code::"+phone);return new R(200,"登录成功",null);}else{return new R(500,"手机号错误",null);}}return new R(500,"验证码错误",null);}

3. 热点数据缓存

为了把一些经常访问的数据,放入缓存中以减少对数据库的访问频率。从而减少数据库的压力,提高程序的性能。【内存中存储】

3.1 缓存的原理

在这里插入图片描述

3.2 java使用redis如何实现缓存功能

在增删改查中模拟redis缓存

@Service
public class StockServiceImpl02 implements StockService {@Autowiredprivate StockDao stockDao;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic Stock getById(Integer id) {//1.查询redis缓存是否命中ValueOperations<String, Object> forValue = redisTemplate.opsForValue();Object o = forValue.get("stock::" + id);//System.out.println(o);//表示缓存命中if(o!=null){return (Stock) o;}//查询数据库Stock stock = stockDao.selectById(id);if(stock!=null){forValue.set("stock::" + id,stock);}return stock;}@Overridepublic Stock insert(Stock stock) {int insert = stockDao.insert(stock);return stock;}@Overridepublic Stock update(Stock stock) {//修改数据库int i = stockDao.updateById(stock);if(i>0){//修改缓存redisTemplate.opsForValue().set("stock::"+stock.getProductid(),stock);}return stock;}@Overridepublic int delete(Integer productid) {int i = stockDao.deleteById(productid);if(i>0){//删除缓存redisTemplate.delete("stock::"+productid);}return i;}
}

发现在查询时会访问redis缓存,如果命中则直接返回数据,未命中则查询数据库并放入redis缓存;在修改时保持数据一致,需要redis中数据一起改变;在删除时,数据库和redis缓存一起删除。

3.2 使用缓存注解完成缓存功能

使用AOP面向切面编程—spring缓存使用的组件

配置文件

 @Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化.disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}

在主函数上需要启动:@EnableCaching

修改上述代码

@Service
public class StockServiceImpl implements StockService {@Autowiredprivate StockDao stockDao;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;//Cacheable:表示查询时使用的注解。 cacheNames:缓存的名称  key:缓存的唯一表示值//   1. 查询缓存中是否存在名称为cacheNames::key的值//   2. 如果存在则方法不会执行//   3. 如果不存在则执行方法体并把方法的返回结果放入缓存中cacheNames::key@Cacheable(cacheNames ={ "stock"}, key = "#id")@Overridepublic Stock getById(Integer id) {Stock stock = stockDao.selectById(id);return stock;}@Overridepublic Stock insert(Stock stock) {int insert = stockDao.insert(stock);return stock;}//CachePut:表示修改时使用的注解.// 1. 先执行方法体// 2. 把方法的返回结果放入缓存中@CachePut(cacheNames = "stock", key = "#stock.productid")@Overridepublic Stock update(Stock stock) {int i = stockDao.updateById(stock);return stock;}//CacheEvict:表示删除时使用的注解// 1. 先执行方法体// 2. 把缓存中名称为cacheNames::key的值删除@CacheEvict(cacheNames = "stock", key = "#productid")@Overridepublic int delete(Integer productid) {int i = stockDao.deleteById(productid);return i;}
}

4. 分布式锁

为了模拟高并发:---使用jmeter压测工具

模拟客户端请求环境

在这里插入图片描述

 public String decrement(Integer productid) {//根据id查询商品的库存int num = stockDao.findById(productid);if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}
}

运行结果:发现出现超卖重卖现象

在这里插入图片描述

解决办法:我们使用synchronized或者lock锁

public String decrement(Integer productid) {//根据id查询商品的库存synchronized (this) {int num = stockDao.findById(productid);if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}}
}

再次测试:发现超卖重卖现象没有发生

在这里插入图片描述

上面使用syn和lock虽然解决了并发问题,但是我们未来项目部署时可能要部署集群模式。

在springboot模拟项目集群

在这里插入图片描述

在这里插入图片描述

接下来使用nginx代理集群

在nginx.conf配置文件中设置代理服务器

在这里插入图片描述

启动nginx,再次压测发现依旧会超卖

在这里插入图片描述

在多线程环境中上述的锁对象是本地锁,每个服务器都有本地锁,导致锁不是唯一的

通过压测发现本地锁 无效了。使用redis解决分布式锁文件

在这里插入图片描述

引入redis依赖和配置

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
spring.redis.host=172.16.7.110
spring.redis.port=6379

修改原有代码

Redis提供了一个命令setnx 可以来实现分布式锁,该命令只在键 key 不存在的情况下 将键 key 的值设置为 value ,若键 key 已经存在, 则 SETNX 命令不做任何动作。根据这一特性我们就可以制定Redis实现分布式锁的方案了。

@Autowired
private StringRedisTemplate redisTemplate;//
public String decrement(Integer productid) {ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();//1.获取共享锁资源Boolean flag = opsForValue.setIfAbsent("product::" + productid, "1111", 30, TimeUnit.SECONDS);//表示获取锁成功if(flag) {try {//根据id查询商品的库存int num = stockDao.findById(productid);if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}finally {//释放锁资源redisTemplate.delete("product::"+productid);}}else{//休眠100毫秒 在继续抢锁try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}return decrement(productid);}}

再次压测

在这里插入图片描述

上述发现并没有超卖重卖,,但是

锁超时问题
这里有一个问题,如果获取到锁的服务在执行方法体(释放锁的时候)宕机了,那锁不就释放不了么,别的服务也就没办法获取到锁,就造成了死锁。

使用redisson

在执行方法体的时候锁的时间到了或者宕机,watch dog会检测持有锁的进程给其增加锁时间(大约3次如果还没执行完或者宕机,该线程会结束)可以自动删除锁,别的服务就可以获取锁了,

引入依赖

    <!--ression依赖--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.24.3</version></dependency>

设置配置文件

@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redisson(){Config config = new Config();
//        //连接的为redis集群
//        config.useClusterServers()
//                // use "rediss://" for SSL connection
//                .addNodeAddress("redis://127.0.0.1:7181","","","")
//        ;//连接单机config.useSingleServer().setAddress("redis://192.168.111.188:6379");RedissonClient redisson = Redisson.create(config);return redisson;}
}

修改原有代码

 @Autowiredprivate RedissonClient redisson;public String decrement(Integer productid) {RLock lock = redisson.getLock("product::" + productid);lock.lock();try {int num = stockDao.findById(productid);if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}} finally {lock.unlock();}}

可以解决该问题。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vs code解决报错 (c/c++的配置环境 远端机器为Linux ubuntu)

参考链接&#xff1a;https://blog.csdn.net/fightfightfight/article/details/82857397 https://blog.csdn.net/m0_38055352/article/details/105375367 可以按照步骤确定那一步不对&#xff0c;如果一个可以就不用往下看了 目录 一、检查一下文件扩展名 二、安装扩展包并…

C#,.NET常见算法

1.递归算法 1.1.C#递归算法计算阶乘的方法 using System;namespace C_Sharp_Example {public class Program{/// <summary>/// 阶乘&#xff1a;一个正整数的阶乘Factorial是所有小于以及等于该数的正整数的积&#xff0c;0的阶乘是1&#xff0c;n的阶乘是n&#xff0…

WPF MVVM框架:CommunityToolkit.Mvvm包使用介绍

最近在需要使用MVVM框架的时候才发现MvvmLight作者宣布停止更新了&#xff0c;有点可惜。 原作者推荐使用CommunityToolkit.Mvvm包&#xff0c;所以这里做一个CommunityToolkit.Mvvm包使用的全面的总结。 开发环境&#xff1a; Visual Studio 2019Windows 10 1903CommunityTo…

四、GD32 MCU 常见外设介绍 (5) TIMER 模块介绍

5.1.TIMER 基础知识 TIMER分高级定时器&#xff0c;通用定时器L0&#xff0c;L1&#xff0c;L2和基本定时器。 5.2.硬件连接说明 TIMER 属于片内外设&#xff0c;对于外部硬件设计&#xff0c;只需要单独IO口外接信号线即可。 5.3.GD32 TIMER 外设原理简介&#xff08;以 G…

电脑屏幕录制软件,分享4款(2024最新)

在今天&#xff0c;我们的电脑屏幕成为了一个多彩多姿的窗口。通过它我们可以浏览网页、观看视频、处理文档、进行游戏……有时&#xff0c;我们想要记录下这些精彩瞬间&#xff0c;与朋友分享&#xff0c;或者作为教程留存&#xff0c;这时&#xff0c;电脑屏幕录制就显得尤为…

全年销售7亿块,巧克力企业如何通过相邻业务打造极致产品力?

蒂罗尔巧克力是日本经典的巧克力品牌。 糖果业务是松尾早期的主营业务&#xff0c;在主营业务下滑的情况下&#xff0c;确立新的竞争方向&#xff0c;通过主营业务优势进入相邻业务&#xff0c;打造新产品成就巧克力极致产品力&#xff0c;避免衰退重回增长。 如何通过进入相邻…

Ubuntu下载jdk:cannot execute binary file

虚拟机上Ubuntu系统安装jdk且配置环境之后&#xff0c;java -version显示cannot execute binary file&#xff0c;多番查阅推测是由于系统和jdk版本不兼容的原因。 uname -m查看系统版本位i686&#xff0c;是32位的&#xff0c;和64位的jdk版本不兼容。因此&#xff0c;下载32位…

Linux:core文件无法生成排查步骤

1、进程的RLIMIT_CORE或RLIMIT_SIZE被设置为0。使用getrlimit和ulimit检查修改。 使用ulimit -a 命令检查是否开启core文件生成限制 如果发现-c后面的结果是0&#xff0c;就临时添加环境变量ulimit -c unlimited&#xff0c;之后在启动程序观察是否有core生成&#xff0c;如果…

【学习笔记】解决Serial Communication Library编译问题

【学习笔记】解决编译 Serial Communication Library 时的 Catkin 依赖问题 Serial Communication Library 是一个用 C 编写的用于连接类似 rs-232 串口的跨平台库。它提供了一个现代的 C 接口&#xff0c;它的工作流程设计在外观和感觉上与 PySerial 相似&#xff0c;但串口速…

Axure软件新功能解析与应用技巧分享

Axure是一种用于创建原型和交互设计的软件工具&#xff0c;广泛应用于操作界面。&#xff08;UI&#xff09;和客户体验&#xff08;UX&#xff09;为了展示和测试应用程序、网站或其他数据产品的性能和操作界面&#xff0c;设计帮助产品经理、设计师和开发者制作具有交互性的原…

服务器上使用Docker部署sonarQube,并集成到Jenkins实现自动化。

目标是要在目标服务器上使用docker工具部署好sonar环境&#xff0c;然后再集成到Jenkins中实现自动化的代码审查工作。 Docker 首先Dokcer的源大部分现在都用不了&#xff0c;于是我上网查询&#xff0c;终于找到了一个可用的镜像。 编辑/etc/docker/daemon.json文件&#x…

lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

这段Lua脚本定义了一个名为 ai_autofight_find_way 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十八章 驱动模块编译进内核

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

Golang | Leetcode Golang题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; // 判断是否为完全平方数 func isPerfectSquare(x int) bool {y : int(math.Sqrt(float64(x)))return y*y x }// 判断是否能表示为 4^k*(8m7) func checkAnswer4(x int) bool {for x%4 0 {x / 4}return x%8 7 }func numSquares(n int) i…

【快速逆向二/无过程/有源码】掌上高考—2024高考志愿填报服务平台

逆向日期&#xff1a;2024.07.21 使用工具&#xff1a;Node.js 加密工具&#xff1a;Crypto-js标准库 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&…

深蓝学院 机器人操作系统ROS理论与实践(四)

一、机器人是什么&#xff1f; 机器人是如何组成的&#xff08;控制的角度&#xff09; 二、机器人系统构建 执行机构的实现——机器人底盘、电机、舵机等 驱动系统的实现 内部传感器的实现 控制系统的实现 外部传感系统的实现——摄像头、激光雷达、GPS等 1、连接摄像头 …

STM32——GPIO(点亮LEDLED闪烁)

一、什么是GPIO&#xff1f; GPIO&#xff08;通用输入输出接口&#xff09;&#xff1a; 1.GPIO 功能概述 GPIO 是通用输入/输出&#xff08;General Purpose I/O&#xff09;的简称&#xff0c;既能当输入口使用&#xff0c;又能当输出口使用。端口&#xff0c;就是元器件…

uniapp手写滚动选择器

文章目录 效果展示HTML/Template部分&#xff1a;JavaScript部分&#xff1a;CSS部分&#xff1a;完整代码 没有符合项目要求的选择器 就手写了一个 效果展示 实现一个时间选择器的功能&#xff0c;可以选择小时和分钟&#xff1a; HTML/Template部分&#xff1a; <picker…

【OpenCV C++20 学习笔记】扫描图片数据

扫描图片数据 应用情景图像数据扫描的难点颜色空间缩减&#xff08;color space reduction&#xff09;查询表 扫描算法计算查询表统计运算时长连续内存3种扫描方法C风格的扫描方法迭代器方法坐标方法LUT方法 算法效率对比结论 应用情景 图像数据扫描的难点 在上一篇文章《基…

调度子系统在特定时间执行

时序逻辑调度器设计模式允许您安排Simulink子系统在指定时间执行。以下模型说明了这种设计模式。 时序逻辑调度器图表包含以下逻辑&#xff1a; 时序逻辑调度器的关键行为 时序逻辑调度器图表包含两个状态&#xff0c;它们以不同的速率调度函数调用子系统A1、A2和A3的执行&…