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

分布式锁全家桶

Redis

1. 基本实现

借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发 送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。

 

 

1. 多个客户端同时获取锁(setnx)

2. 获取成功,执行业务逻辑,执行完成释放锁(del)

3. 其他客户端等待重试

@Service
public class StockService {
@Autowired
private StockMapper stockMapper;
@Autowired
private LockMapper lockMapper;
@Autowired
private StringRedisTemplate redisTemplate;
 public void checkAndLock() {
    // 加锁,获取锁失败重试
     while (!this.redisTemplate.opsForValue().setIfAbsent("lock","xxx")){
      try {
        // 避免栈溢出
        Thread.sleep(100);
         
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
     }
         // 先查询库存是否充足
     Stock stock = this.stockMapper.selectById(1L);
     // 再减库存
     if (stock != null && stock.getCount() > 0){
     stock.setCount(stock.getCount() - 1);
     this.stockMapper.updateById(stock);
      }
      // 释放锁
     this.redisTemplate.delete("lock");
 }
}

2. 防死锁

 解决:给锁设置过期时间,自动释放锁。

  设置过期时间两种方式:

  1. 通过expire设置过期时间(缺乏原子性:如果在setnx和expire之间出现异常,锁也无法释放)

  2. 使用set指令设置过期时间:set key value ex 3 nx(既达到setnx的效果,又设置了过期时间)

 

问题:可能会释放其他服务器的锁。

场景:如果业务逻辑的执行时间是7s。执行流程如下

1. index1业务逻辑没执行完,3秒后锁被自动释放。index2请求就会执行

2. index2获取到锁,执行业务逻辑,3秒后锁被自动释放。index3请求就会执行

3. index3获取到锁,执行业务逻辑

4. index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁,导致index3的业务只 执行1s就被别人释放。 最终等于没锁的情况。

解决:setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的 锁 

 3. 防误删

 

 实现如下:

 问题:删除操作缺乏原子性。

场景:

1. index1执行删除时,查询到的lock值确实和uuid相等

2. index1执行删除前,lock刚好过期时间已到,被redis自动释放

3. index2获取了lock

4. index1执行删除,此时会把index2的lock删除

解决方案:没有一个命令可以同时做到判断 + 删除,所有只能通过其他方式实现(LUA脚本)

 4. redis中的lua脚本 保证原子性

1. 现实问题

redis采用单线程架构,可以保证单个命令的原子性,但是无法保证一组命令在高并发场景下的原子性。 例如:

 

在串行场景下:A和B的值肯定都是3

在并发场景下:A和B的值可能在0-6之间。

极限情况下1:

 

则A的结果是0,

B的结果是3

极限情况下2:

 

则A和B的结果都是6

如果redis客户端通过lua脚本把3个命令一次性发送给redis服务器,那么这三个指令就不会被其他客户 端指令打断。Redis 也保证脚本会以原子性(atomic)的方式执行: 当某个脚本正在运行的时候,不会有 其他脚本或 Redis 命令被执行。 这和使用 MULTI/ EXEC 包围的事务很类似。

但是MULTI/ EXEC方法来使用事务功能,将一组命令打包执行,无法进行业务逻辑的操作。这期间有某 一条命令执行报错(例如给字符串自增),其他的命令还是会执行,并不会回滚。

相关文章:

  • 数据库优化(8月27号)
  • 【BurpSuite】插件开发学习之J2EEScan(下)-主动扫描(11-20)
  • json/xml/schema
  • 进程管理学习
  • XMLHttpRequest对象,简单ajax get请求的例子
  • 在 Windows 10 | Docker Desktop | Kubernetes 环境 使用 hostPath / local 为 POD 配置本机目录
  • .NET 服务 ServiceController
  • 卧槽!GitHub排行榜即将下线;酷炫的Python热重载工具;开发者体验·电子书;C++最佳实践合辑;前沿论文 | ShowMeAI资讯日报
  • 基于惯性权值非线性递减的改进粒子群算法 - 附代码
  • 用ARM进行汇编语言编程(4)带有分支的循环和条件指令执行
  • Postgresql源码(77)plpgsql中参数传递和赋值
  • CSAPP datalab
  • Github爆火,阿里最新发布的《高并发核心编程笔记》PDF文档
  • ntohl()、htonl()、ntohs()、htons()函数
  • 基于ssm+vue的师生防疫登记管理系统 elementui
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 【Amaple教程】5. 插件
  • 2017前端实习生面试总结
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • HTML中设置input等文本框为不可操作
  • Idea+maven+scala构建包并在spark on yarn 运行
  • Java 23种设计模式 之单例模式 7种实现方式
  • Meteor的表单提交:Form
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • Python_网络编程
  • Solarized Scheme
  • Spring核心 Bean的高级装配
  • underscore源码剖析之整体架构
  • uni-app项目数字滚动
  • vue2.0项目引入element-ui
  • 分布式事物理论与实践
  • 关于for循环的简单归纳
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 什么软件可以剪辑音乐?
  • 算法系列——算法入门之递归分而治之思想的实现
  • 原生Ajax
  • 正则表达式小结
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 从如何停掉 Promise 链说起
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • ​用户画像从0到100的构建思路
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (02)Hive SQL编译成MapReduce任务的过程
  • (阿里云万网)-域名注册购买实名流程
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (原)Matlab的svmtrain和svmclassify
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始