zoukankan      html  css  js  c++  java
  • Redis实现分布式锁

    布式锁实现的三个核心要素:加锁、解锁、锁超时。

    一、Redis实现分布式锁基本原理

    1.1、Redis具体实现

    情景:存在多台JVM需要同时对某一商品(id)进行操作。

    • 加锁:使用setnx命令,伪代码:setnx(id,value)
        返回1,说明key不存在,线程抢锁成功;
        返回1,说明key已存在,线程抢锁失败。
       注意:setnx(id,value)中key为操作商品的id,value值可用于防止误删锁,下文有提到。

    • 解锁:使用del命令,伪代码:del(id)

    • 锁超时:如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,该资源将会被永远占用,其他线程将无法访问。
         可以使用expire为key设置一个超时时间,与setnx命令一起执行(setnx不支持超时参数),用以保证即使未被显式释放,该锁也可在一定时间后自动释放。伪代码:expire(key, 30)。

    1.2、上述实现存在的问题

    • 非原子性操作
        加锁setnx和锁超时expire两个命令未非原子性操作,当执行加锁setnx后,若因网络或客户端问题锁超时expire命令未成功执行时,锁将无法被释放。
      解决方案:
        使用set命令取代setnx和expire命令。setnx本身不支持设置超时时间。在Redis 2.6.12以上版本为set指令增加了可选参数,伪代码:set(key, value, expire)。

    • 误删锁
      设想如下情形:
        (1)JVM1使用set(001, 002, 30)成功获取锁,并设置超时时间为30s;
        (2)JVM1开始数据处理,处理时间已经超过了30s...
        (3)服务器检测到(001, 002, 30)数据超时,将自动执行del进行数据删除,此时JVM1还在数据处理...
        (4)此时,JVM2使用set(001, 002, 30)成功获取锁,并设置超时时间为30s;
        (5)JVM2开始数据处理。与此同时,JVM1处理完成,操作提交后,根据商品id001,执行了del;
          到此,JVM1成功误删了JVM2的锁。
      解决方案:
        del数据之前,增加锁判断机制:判断要删除的锁是否属于本线程。操作流程:
        (1)加锁:set(id, threadId,expire),其中value为当前线程ID;
        (2)解锁:执行del命令时,根据id和threadId数据判断该锁是否仍属于本线程。是,则删除。

    • 并发问题
        基于误删锁的前提下,由于我们无法确定程序成功处理完成数据的具体时间,这就为超时时间的设置提出了难题。设置时间过长、过短都将影响程序并发的效率。
      解决方案:JVM1需要自己判断在超时时间内是否完成数据处理,如未完成,应请求延长超时时间。具体操作:
        为获取锁的锁的线程开启一个守护线程。当29秒时(或更早),线程A还没执行完,守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,每20秒执行一次。当线程A执行完任务,会显式关掉守护线程。

        另一种情况:如果节点1 忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。当过了超时时间后,没有守护进程的“续命”,锁将自动释放。

  • 作者: DeepInThought
    出处: https://www.cnblogs.com/DeepInThought
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
查看全文
  • 相关阅读:
    webpack.DefinePlugin
    webpack-dev-server配置指南(使用webpack3.0)
    Eclipse配色方案插件
    解决Sublime Text 3中文显示乱码问题(转)
    Java连接SqlServer2008数据库
    [转]java中判断字符串是否为数字的三种方法
    VS2008 SP1 安装卡在 VS90sp1-KB945140-X86-CHS的解决方法
    Python获取桌面路径
    关于fdisk命令
    socket 错误之:OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。
  • 原文地址:https://www.cnblogs.com/DeepInThought/p/11072966.html
  • Copyright © 2011-2022 走看看