美文网首页
分布式锁

分布式锁

作者: YDDMAX_Y | 来源:发表于2020-08-15 16:34 被阅读0次

    分布式锁需要解决下面问题

    1. 互斥性
    2. 续命(比如想延长redis锁的加锁时间)
    3. 锁释放
    4. HA一致性(比如redis主备切换导致锁丢失不互斥了)
      下面列出几种分布式锁的实现和对比。
    方案 互斥性 锁释放 HA一致性 续命 其他优点 其他缺点
    关系型数据库 很好 很好(事务保证) 很好需要配置数据库在完成主备同步之后才返回上层sql成功 不需要续命(不需要像KV方案那样设置expireTime) 互斥性和锁释放指标很好 因为加锁期间整个事务都要维持会造成事务时间很长,暂用宝贵连接
    KV存储 一般(如果获得锁之后因为load高或者网络差等造成业务处理时间超过expireTime会造成别的请求可以再次获得锁,造成锁不互斥) 一般(像应用程序宕机等极端情况需要等待expireTime才能释放锁) 主备同步完成之后才返回生成成功,对于redis可以使用wait 延长expire 互斥性和锁释放相对其他两种方案稍差 互斥性和锁释放在可接受范围内,而且性能很好
    ZK 很好 很好(会话保证) ZAB协议保证 不需要续命(不需要像KV方案那样设置expireTime) 互斥性和锁释放指标很好 ZK的性能较低

    数据库锁(有多种方案,这里只考虑在一个事务里方案)

    1. 加锁
      开启事务并insert
    2. 解锁
      rollback

    KV数据库锁(以redis举例)

    参考:https://help.aliyun.com/document_detail/146758.html

    1. 加锁
      SET resource_1 random_value NX EX 5
    2. 续命
      在Redis中通常需要用Lua脚本来实现自锁自解:
      在Redis中可使用如下Lua脚本来实现续租:
    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("expire",KEYS[1], ARGV[2])
    else
        return 0
    end
    
    1. 解锁
      在Redis中通常需要用Lua脚本来实现自锁自解:
    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    
    1. HA一致性
      Redis的WAIT命令会阻塞当前客户端,直到这条命令之前的所有写入命令都成功从master同步到指定数量的replica,命令中可以设置单位为毫秒的等待超时时间。在云Redis版中使用WAIT命令提高分布式锁一致性的示例如下:
    SET resource_1 random_value NX EX 5
    WAIT 1 5000
    

    使用以上代码,客户端在加锁后会等待数据成功同步到replica才继续进行其它操作,最大等待时间为5000毫秒。执行WAIT命令后如果返回结果是1则表示同步成功,无需担心数据不一致。相比红锁,这种实现方法极大地降低了成本。

    需要注意的是:

    WAIT只会阻塞发送它的客户端,不影响其它客户端。
    WAIT返回正确的值表示设置的锁成功同步到了replica,但如果在正常返回前发生高可用切换,数据还是可能丢失,此时WAIT只能用来提示同步可能失败,无法保证数据不丢失。您可以在WAIT返回异常值后重新加锁或者进行数据校验。
    解锁不一定需要使用WAIT,因为锁只要存在就能保持互斥,延迟删除不会导致逻辑问题。

    ZK锁

    每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。
    判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。
    当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

    相关文章

      网友评论

          本文标题:分布式锁

          本文链接:https://www.haomeiwen.com/subject/zdyadktx.html