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

【redis】redis分布式锁

目录

        • 一、为什么需要分布式锁
        • 二、分布式锁的实现方案
        • 三、redis分布式锁
          • 3.1 简单实现
          • 3.2 成熟的实现

一、为什么需要分布式锁

  • 1.在java单机服务中,jvm内部有一个全局的锁监视器,只有一个线程能获取到锁,可以实现线程之间的互斥
  • 2.当有多个java服务时,会有多个jvm,也会有多个锁监视器,这样没办法使得多个jvm之间的线程互斥,所以无法使用jvm内部的锁监视器,也就是synchronized关键字无法用于此场景
  • 3.需要在多个jvm外部加一个锁监视器,这样保证不同的jvm的线程保持互斥
  • 4.满足分布式系统或者集群模式下多个进程都可见并且互斥的锁
  • 5.分布式锁满足的特性:多进程可见、互斥、高可用、高性能、安全
  • 6.分布式锁是否可重入
  • 7.分布式锁是否公平

二、分布式锁的实现方案

  • 1.mysql实现:利用mysql内部的写操作(本身的互斥锁),可以开启一个事务,执行业务,业务执行完之后,提交事务释放锁,当业务出现异常,事务回滚,释放锁。可用性好,性能一般,断开连接也会释放锁,是安全的
  • 2.redis实现:利用setnx的互斥命令,当key存在的时候set值会失败,当key不存在的时候才会set成功,删除key之后,锁便能释放。redis集群,可用性得到保障,高性能。给key加上过期时间,即使服务器宕机,redis重启,key到期失效锁也会自动失效,解决安全性问题。key的过期时间也是有讲究的,如果过短,业务还未处理完便释放了锁,会有问题。如果过长,无效的等待时间会变多。
  • 3.zookeeper实现:利用节点的唯一性和有序性实现互斥。zookeeper可以集群,可用性好,zookeeper是强一致性,性能上比redis差,节点是临时节点,服务器宕机,节点断开连接会自动释放

三、redis分布式锁

3.1 简单实现
  • 1.获取锁:setnx lock thread,lock不存在则设置key,lock存在则设置不了lock
  • 2.释放锁:del key,如果服务宕机,没有del key,会导致key一直存在,造成死锁
  • 3.设置过期时间:expire lock 10,lock在10秒后自动过期
  • 4.若setnx lock thread之后,还没来得及expire lock 10,服务器宕机,仍然会死锁,是用一个set命令保证原子性,即set lock thread ex 10 nx
  • 5.获取锁失败会有两种机制,一是继续等待,等到有线程释放锁为止,阻塞式获取,二是非阻塞是获取,如果获取失败,会尝试再获取一次,如果还是失败则立即结束返回结果。阻塞式实现比较麻烦,对cpu也会产生压力
  • 6.非阻塞式分布式锁实现过程:尝试获取锁,获取锁成功,执行业务,业务如果超时或者宕机则释放锁,业务执行完毕也会释放锁
  • 7.误删锁情况:线程1获取到锁,由于业务处理时间过长,锁超时释放,此时线程2获取到锁,执行业务,业务时间也长,线程1此时业务处理完毕,然后释放了锁,线程3此时拿到锁执行业务,那么此时线程2和线程3的执行业务就是非互斥的,造成并发不安全。
  • 8.对于无删除锁情况,锁的value中应该存一份线程的标识(可以用uuid,若是线程的id,集群情况下会发生线程id重复的情况),实现过程:线程1获取到锁,并存入线程标识,获取锁成功,开始执行业务,若业务超时或服务宕机,自动释放锁,若业务执行完毕且没有超时,判断锁标识是否是线程1的,如果是则释放锁
  • 9.非原子操作情况:线程1获取到锁,然后处理业务,业务处理完毕,准备释放锁,判断锁标识是否是线程1的,判断是的,开始释放锁,此时线程1阻塞(jvm发生FullGC),导致redis锁超时释放,GC完毕,线程2获取到锁,开始执行业务,业务时间较长,线程1阻塞结束,释放掉了锁,线程3获取到锁,执行业务,此时线程2和线程3的业务处理产生并发问题。所以需要线程1判断锁标识的操作和释放锁的操作要保证原子性,需要用lua脚本实现
  • 10.还有其它问题,例如不可重入,同一个线程无法多次获取同一个锁;不可重试,获取锁只尝试一次就返回false,没有重试机制;超时释放,超时释放可以避免死锁,但业务处理时间过长,导致锁超时释放,会存在安全隐患;主从一致性问题,如果redis提供了主从集群,线程1在主机上设置了锁,主从同步存在延迟,当主机宕机时,从机没有同步到主机上的锁数据,线程2读从机时未发现到锁,线程2也能拿到锁,此时从机被推选为主机,并缓存了线程2的锁,此时线程1和线程2在业务处理上存在并发问题
3.2 成熟的实现
  • 1.开源框架redission

http://www.kler.cn/a/7368.html

相关文章:

  • ARM(安谋) China处理器
  • 深入理解Flutter生命周期函数之StatefulWidget(一)
  • T265相机双目鱼眼+imu联合标定(全记录)
  • Hybird和WebView
  • 基本数据类型和包装类型的区别、缓存池、自动拆箱装箱(面试题)
  • 双子数(枚举素数)
  • 镜头光学指标介绍----清晰度SFR
  • 【从零开始学习 UVM】10.2、UVM TLM —— UVM TLM Blocking Put Port
  • 【CSAPP】进程 | 上下文切换 | 用户视角下的并发进程
  • 流量整形(GTS和LR)
  • 蓝桥杯之单片机学习(终)——关于之前文章的错误及更正(附:第十四届蓝桥杯单片机赛题)
  • L2-040 哲哲打游戏 简单模拟
  • 免费CRM如何进行选择?
  • 用GPT-4写代码不用翻墙了?Cursor告诉你:可以~~
  • 【视频分割】【深度学习】MiVOS官方Pytorch代码-S2M模块DeepLavV3Plus网络解析
  • 【Vue框架】Vue绑定样式及案例之行内样式——对象绑定样式与数组控制样式(附带源码案例)
  • 前端基础-ES6
  • 网络安全行业现在好混吗,工资水平怎么样?
  • Junit 5 单元测试框架
  • Matlab 一种计算植物面积密度的新方法(论文复现:凸包法)
  • 【C++】用一棵红黑树同时封装出map和set
  • 2022年业绩逆势增长,“要强”蒙牛再创蒙牛
  • Flutter 本地SQLite数据库版本升级处理
  • 数据分析之Pandas(2)
  • 【Go基础】一篇文章带你了解 — map
  • Lock wait timeout exceeded; try restarting transaction