美文网首页
分布式锁

分布式锁

作者: 格林哈 | 来源:发表于2021-05-29 17:04 被阅读0次

1 方案

  • 数据库实现
  • redis 实现
  • zookeper 实现

1.1 分布锁的问题

  • 可用问题:无论何时都要保证锁服务的可用性
  • 死锁问题:客户端一定可以获得锁,即使锁住某个资源的客户端在释放锁之前崩溃或者网络不可达
  • 脑裂问题:集群同步时产生的数据不一致,导致新的进程有可能拿到锁,但之前的进程以为自己还有锁,那么就出现两个进程拿到了同一个锁的问题

1.2 锁的设计原则

  • 互斥性 同一时刻只有一个线程获取锁
  • 高可用 集群保证高可用
  • 锁释放 具备锁的失效机制,防止死锁
  • 可重入 一个节点获取锁之后,还可以再次获取锁。

2 redis 实现


# 成功返回OK 表示持有锁, 失败返回null 获取锁失败
# 获取锁(unique_value可以是UUID等)
SET resource_name unique_value NX PX 3000
    NX 只在键不存在时, 才对键进行设置操作
    PX 将键的过期时间设置为 milliseconds 毫秒
    EX 将键的过期时间设置为 seconds 秒

# f封装到一个命令 释放锁(lua脚本中,一定要比较value,防止误解锁)
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

redis.call() 执行命令 ,过程中发生错误时,脚本会停止执行
redis eval  可以执行脚本, 所有键都应该由 KEYS 数组来传递
如: eval "return redis.call('set',KEYS[1],'bar')" 1 xxp
# 在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在 Lua 中通过全局变量 ARGV 数组访问
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second

  • 缺点
    • 只能作用在一个节点,即使通过sentinel 保证高可用,master节点主从切换 可能出现丢失情况
      • master 拿到锁
      • 锁没有同步到slave节点
      • master 故障,发生故障转移,slave节点升级为master节点
      • 导致锁丢失

2.1 redis作者Redlock实现

  • 获取锁的算法 假设当前有5个节点
    • 获取当前时间戳,单位毫秒
    • client 按照顺序使用key,value 获取所有redis服务的锁,客户端设置一个网络连接和响应超时时间,这个超时时间要小于锁的失效时间。
      -例如 锁失效 10s,超时时间 5-50ms,比较服务器redis挂掉,客服端等待,应该尽快去访问下个redis实例
    • 当大多数(n/2+1,这里是三个)的redis节点获取到锁,并且 使用的时间小于锁失效时间获取锁成功
      • 使用的时间 = 当前时间-开始获取锁的时间 ,即步骤1获取的时间戳
    • 获取到锁,key真正有效时间 = 有效时间 - 获取锁使用时间
    • 获取锁失败,客户端应该对所有redis实例进行解锁
      • 获取锁失败, 没有n/2+1实例获取到锁,或者锁 有效时间 超过 获取锁的时间
      • 解锁:因为小于3个锁,必须释放,或者影响其他client 获取锁

2.2 基于 Redis 实现分布式锁的优缺点

  • 优点
    • 性能高效
    • 实现方便
    • 避免单点故障
  • 缺点
    • 不合理设置超时时间,
      • 锁的超时时间设置过长,会影响性能,如果设置的超时时间过短,有可能业务阻塞没有处理完成
      • 解决方案
        • 基于续约的方式设置超时时间:先给锁设置一个超时时间,然后启动一个守护线程,让守护线程在一段时间后,重新设置这个锁的超时时间
    • Redis 集群的数据同步机制,都会导致分布式锁的不可靠性( redislock 解决了这个问题)
      • Redis 集群数据同步到各个节点时是异步的,如果在 Redis 主节点获取到锁后,在没有同步到其他节点时,Redis 主节点宕机了,此时新的 Redis 主节点依然可以获取锁,所以多个应用服务就可以同时获取到锁

3 数据库

  • 悲观锁
    • select id from order where order_id = xxx for update
  • 乐观锁
## SELECT 同时获取 ver 值
select amount, old_ver from order where order_id = xxx
## UPDATE 的时候检查 ver 值是否与第 2 步获取到的值相同
update order set ver = old_ver + 1, amount = yyy where order_id = xxx and ver = old_ver

4 常见问题

4.1 分布式锁高并发优化

  • 分段加锁 + 合并扣减
    • 例如 某个商品库存字段 10000,扩建成10个库存字段,每个1000。
      • 就可以有10把锁, 请求过来随机选择一个key,去加锁就行了。
      • 如果某个持有锁,库存不够,可以查别的库存。

参考

相关文章

  • 分布式锁

    为什么要用分布式锁 数据库乐观锁redis分布式锁zookeeper分布式锁 使用分布式锁的场景 实现分布式锁的方...

  • 什么是分布式锁?几种分布式锁分别是怎么实现的?

    一、什么是分布式锁: 1、什么是分布式锁: 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资...

  • 4:Redis 分布式锁 (文末有项目连接)

    1:什么是缓存分布式锁 2:分布式锁的关键代码 3:业务代码使用分布式缓存锁 4:业务代码使用分布式缓存锁 5:测...

  • 锁(2)-- 分布式锁

    前言: 锁分3种:java锁、分布式锁、DB锁 分布式锁的几种实现方式 目前几乎很多大型网站及应用都是分布式部署...

  • java锁的概念

    参考文档探究分布式并发锁并发编程-锁的发展和主流分布式锁比较总结从构建分布式秒杀系统聊聊分布式锁探索并发编程(六)...

  • Redis实现分布式锁

    分布式下的分布式锁一般实现有三种: 基于数据库的乐观锁 基于redis的分布式锁 基于zookeeper的分布式锁...

  • 分布式锁

    为什么要用分布式锁? 分布式锁是悲观锁的实现; 如果采用乐观锁的方案就用不着分布式锁了。 能用乐观锁的地方尽量用乐...

  • 3.10:分布式锁

    本文将梳理微服务架构下,分布式锁的常用方案。整体包含以下三部分: 分布式锁的提出 分布式锁主流方案 分布式锁选择 ...

  • Redis实现分布式锁

    1. 分布式锁分类 数据库乐观锁 基于Redis的分布式锁 基于ZooKeeper的分布式锁 2. 组件依赖 po...

  • 大佬浅谈分布式锁

    redis 实现 redis 分布锁一、redis 实现分布式锁(可重入锁)redission 实现分布式锁1、对...

网友评论

      本文标题:分布式锁

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