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

redis实战---分布式锁--单机篇

redis分布式锁

  • 故事背景
  • 什么是Redis分布式锁
  • 业务场景
    • 未加任何锁的代码
  • 单机情况下JVM级别加锁
  • 多服务部署
  • 总结提升

故事背景

本篇文章是redis实战系列的第二篇文章。本章的主要内容是Redis分布式锁的相关知识。本篇文章将告诉你什么是分布式锁,结合一个业务场景,先带大家看看,单机上是如何实现锁功能的。学完本篇,你可以了解到什么是锁,为什么要加锁。

什么是Redis分布式锁

Redis分布式锁是一种基于Redis实现的分布式锁机制,它可以保证在分布式环境中,同一时刻只有一个客户端能够获取到锁,从而避免了多个客户端同时对同一资源进行修改的问题。
在这里插入图片描述
接下来我将结合一个秒杀的例子讲述如果实现Redis的分布式锁。

业务场景

秒杀场景是一个非常经典的需要使用锁的场景。
假设有一个商品限时秒杀的业务场景,多个用户同时在秒杀开始时间内尝试购买该商品,但是该商品数量有限,只有一定数量的用户可以购买成功,其他用户则购买失败。
为了保证秒杀的公平性与真确性,这个时候我们就要通过来对商品的数量进行访问

未加任何锁的代码

结合上面的业务场景,我们来先来实现一个未加任何锁的代码,简单实现一下这个小需求:

  1. 首先在redis里添加了 key值为 stock value值 为 200 的数据,模拟我们要秒杀的商品数量为200
    在这里插入图片描述
    2.编写业务逻辑代码
@RestController
@RequestMapping("/test")
public class IndexController {// 自动注入 StringRedisTemplate 对象@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 处理 HTTP GET 请求路径是 /test/lock@GetMapping("lock")public String deductStock() {// 获取当前库存String stock1 = stringRedisTemplate.opsForValue().get("stock");if( stock1 == null){System.out.println("秒杀未开始");return "end";}int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock > 0) {// 扣减库存int realStock = stock - 1;// 更新库存stringRedisTemplate.opsForValue().set("stock", realStock + "");System.out.println("扣减成功,剩余的库存为:" + realStock);} else {System.out.println("扣减失败,库存不足");}return "end";}}

上述是根据我们的业务进行的一个简单的实现,在这个实现里,未对代码进行加锁。这段代码将会出现很经典的超卖问题。我们来压测一下接口,看一下效果
3. 接口压测,模拟并发
在这里插入图片描述
在这里我们使用的ApiPost进行一键压测。让我们一起来看一下结果吧
在这里插入图片描述
我们发现,50个请求进来之后,如果是正常的情况下,是应该减少50个库存,每个请求获得1个商品。可以根据结果看,我们的50个请求获得了5个商品。同一个商品卖给了多个用户。列如 195号商品同时卖给了10个人。
那么我们该如何去解决这个问题呢?

单机情况下JVM级别加锁

首先我们来看一下,如果是单机(项目只部署在一台机器上),如何给我们的代码加锁,解决上述问题。
使用 synchronized 进行jvm级别加锁。

  1. 代码
synchronized (this){// 获取当前库存String stock1 = stringRedisTemplate.opsForValue().get("stock");if( stock1 == null){System.out.println("秒杀未开始");return "end";}int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock > 0) {// 扣减库存int realStock = stock - 1;// 更新库存stringRedisTemplate.opsForValue().set("stock", realStock + "");System.out.println("扣减成功,剩余的库存为:" + realStock);} else {System.out.println("扣减失败,库存不足");}}

代码非常的简单,使用 synchronized 关键字,将我们的业务逻辑进行包裹即可。
synchronized,保证同一时刻只有一个线程执行被synchronized修饰的代码块或方法,从而避免多个线程同时对共享资源进行修改而导致的数据不一致的问题。
2. 运行结果
在这里插入图片描述
从结果上来看,通过synchronized 可以在jvm级别上进行上锁。但是我们实际的生产环境中,很少有部署单机服务的。如果我们部署了多个服务,那么通过synchronized 是肯定无法影响另一条机器上的请求的。

多服务部署

在这里插入图片描述
假设我们,部署了两个服务,部署在tomcat1和tomcat2上,使用nginx做负载。此时仅仅通过synchronized 只能保持 tomcat1自己本身。tomcat2自己本身的数据被锁住。如果两个服务同时提供服务,仍然会产生我们上述的超卖问题。

总结提升

本文我们主要讲了锁的概念,为什么要加锁,单机上jvm级别的加锁,多服务部署的话,我们现在的代码存在的问题。接下来我会讲解如何解决我们这次遗留的问题,在分布式环境下,如何加锁,如何解决可能会存在的问题。
专栏地址

http://www.lryc.cn/news/44647.html

相关文章:

  • Java正则表达式
  • MySQL数据库之——高级[进阶]SQL语句(二)正则表达式和存储过程
  • Python基于周立功盒子的二次开发的准备工作
  • 2023年PMP考生|考前必练全真模拟题分享,附答案解析
  • Python入门教程+项目实战-7.1节: 条件控制结构
  • 【机器学习】P4 特征缩放与学习率
  • 《Python编程:从入门到实战》(第2版)学习笔记 第11章 测试代码
  • SpringBoot(1)基础入门
  • 利用Flow Simulation快速经济高效地解决传热难题
  • 揭开二维码背后的神秘面纱用二维码识别 API 就够了
  • 系统分析——系统构建最重要的一环
  • 第1-第20个高级shell程序
  • 【致敬嵌入式攻城狮第2期活动预热征文】学习安排
  • 035:cesium加载KML文件,显示图形
  • 随想录Day42--动态规划: 416. 分割等和子集(终于吃下01背包了)
  • 字节跳动软件测试岗,前两面过了,第三面被面试官吊打,结局我哭了
  • bitlocker 笔记
  • Linux 压缩与解压命令
  • python global函数用法及常用的 global函数代码
  • 大数据学完好就业么
  • CASAtomic 原子操作详解
  • 卷积神经网络(convolutional neural network, CNN)
  • kube-apiserver启动流程源码分析
  • Scala基础(二)
  • Python 生产者消费者模型是什么?
  • 手机银行评测系列:北京银行“京彩生活”7.0从用户视角出发,实现沉浸式体验重塑
  • ZJYC2023 浙江省大学生程序设计竞赛校内选拔赛部分题解 C J B L
  • 百科创建:7种有效的百科词条创建技巧
  • ThreeJS-dat.gui界面控制颜色、隐藏、位置(六)
  • 接口自动化测试,完整入门篇