当前位置:   article > 正文

常见分布式锁_分布式锁 租约

分布式锁 租约

分布式锁的特性

互斥性
在任意时刻,只有一个客户端持有锁。

不死锁
分布式锁本质上是一个基于租约(Lease)的租借锁,如果客户端获得锁后自身出现异常,锁能够在一段时间后自动释放,资源不会被锁死。

一致性
硬件故障或网络异常等外部问题,以及慢查询、自身缺陷等内部因素都可能导致Redis发生高可用切换,replica提升为新的master。此时,如果业务对互斥性的要求非常高,锁需要在切换到新的master后保持原状态。

数据库

乐观:基于表字段版本号做分布式锁

这个策略源于 mysql 的 mvcc 机制,使用这个策略其实本身没有什么问题,唯一的问题就是对数据表侵入较大,我们要为每个表设计一个版本号字段,然后写一条判断 sql 每次进行判断,增加了数据库操作的次数,在高并发的要求下,对数据库连接的开销也是无法忍受的。

悲观:基于数据库排他锁做分布式锁

for update

● 阻塞锁? for update语句会在执行成功后立即返回,在执行失败时一直处于阻塞状态,直到成功。
● 锁定之后服务宕机,无法释放?使用这种方式,服务宕机之后数据库会自己把锁释放掉

问题1:InnoDB 引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁
解决1:添加唯一索引

问题2:MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引
解决2:增大表数据

问题3:长时间占用数据库链接

悲观:基于表主键唯一做分布式锁

向数据库插入请求信息获取锁,释放时删除

特点1:依赖数据库可用性
解决1:主备双向同步

特点2:无失效时间
解决2:定时任务清理

特点3:非阻塞
解决3:循环获取

特点4:非重入
解决4:记录主机和线程信息

特点5:非公平
解决5:中间表记录等待线程

redis

SET resource_1 random_value NX EX 5
  • 1

示例代码为resource_1这个key设置了5秒的过期时间,value作为唯一标识,如果客户端不释放这个key,5秒后key将过期,锁就会被系统回收,此时其它客户端就能够再次为资源加锁并访问资源了。

优点

  1. 性能高
  2. 实现简单

缺点

问题1:自锁自解

解锁需要判断当前线程是否持有这个锁再删除
在这里插入图片描述
解决:
用Lua脚本实现原子性

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
  • 1
  • 2
  • 3
  • 4
  • 5

问题2:续租

当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。在Redis中可使用如下Lua脚本来实现续租:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("expire",KEYS[1], ARGV[2])
else
    return 0
end
  • 1
  • 2
  • 3
  • 4
  • 5

问题3:单点

使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。

  1. 尝试从 N 个互相独立 Redis 实例获取锁;
  2. 计算获取锁消耗的时间,只有时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,才认为获取锁成功;
  3. 如果获取锁失败,就到每个实例上释放锁。

极端情况仍有可能产生问题

zookeeper

原理

模型

Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节点为 /app1。在这里插入图片描述

节点类型

永久节点:不会因为会话结束或者超时而消失;
临时节点:如果会话结束或者超时就会消失;
有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,以此类推。

监听器

为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。

分布式锁实现

  1. 创建一个锁目录 /lock;
  2. 当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点;
  3. 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
  4. 执行业务代码,完成后,删除对应的子节点

优点

  1. 无单点问题
  2. 可重入问题
  3. 非阻塞
  4. 临时节点,会话超时将被删除,解决释放问题

缺点

  1. 性能上可能并没有缓存服务那么高,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能。ZK 中创建和删除节点只能通过 Leader 服务器来执行,然后将数据同步到所有的 Follower 机器上。
  2. 复杂度高。需要对 ZK的原理有所了解。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/776233
推荐阅读
相关标签
  

闽ICP备14008679号