1 什么是分布式锁
分布式锁其实就是控制分布式系统不同进程共同访问共享资源的一种锁的实现。分布式系统共享资源进行访问时,一般都需要添加互斥锁防止干扰,保证一致性。
2 分布式实现方案
- 基于数据库实现的分布式锁
- 基于Redis实现的分布式锁
- 基Zookeeper实现的分布式锁
2.1 基于数据库实现的分布式锁
2.1.1 悲观锁
使用 select .....for update 来实现分布式锁。其原理为:select ... for update 锁定数据所在记录所在行,添加行锁(注意,必须添加事务才能生效),这是一种悲观锁
2.1.2 乐观锁
表字段添加version字段列,每次更新时,通过CAS 比较version值进行数据更新,一般需要添加自旋
2.2 基于Redis实现的分布式锁
2.2.1 setnx
使用的是set的扩展命令(set ex px nx)
- EX second :设置键的过期时间为 second 秒。
- PX millisecond :设置键的过期时间为 millisecond 毫秒。
- NX :只在键不存在时,才对键进行设置操作。
- XX :只在键已经存在时,才对键进行设置操作。
此方案可能存在的问题:
- 锁过期了,业务还没执行完
- 锁被其他线程删除了(可以通过添加唯一标识key解决)
2.2.2 Redisson
为了解决锁过期了,业务还没执行完的问题,Redisson会给获取到锁的线程,单独起一个后台线程,每隔10秒检查一下当前线程是否还持有锁,若仍持有,则延长key的锁生存时间
2.2.3 RedLock
根本思想是:部署多个Redis Master节点,以保障他们不会同时宕机,每个节点之间配置保持一致,数据不需要进行同步,在获取锁时,同时向所有节点发送加锁请求,若超过半数的节点加锁成功,则认为加锁成功,否则,加锁失败,已经加锁的节点,需要执行解锁
2.3 基于Zookeeper实现的分布式锁
首先需要了解ZK的节点相关知识,ZK节点Znode分为4种类型:
- 持久节点
默认的节点类型,创建节点的客户端与zk断开连接后,该节点仍然存在
- 持久节点顺序节点
所谓的顺序节点就是在创建节点时,ZK根据创建的时间顺序给该节点名称进行编号,持久节点顺序节点就是有序的持久节点
- 临时节点
和持久节点相反,客户端和zk断开连接后,临时节点会被删除
- 临时节点顺序节点
有序的临时节点
Zookeeper使用临时顺序节点实现了分布式锁。
- 第一个客户端请求过来时 ,zk客户端会创建一个持久节点locks,如果该客户端(Client1)想要获取锁,需要在locks下创建一个顺序节点 lock1
- 客户端(Client1)会查找locks下的所有临时顺序节点,判断自己持有的节点lock1是否是排序最小的那个,如果是,则获取锁成功
- 第二个客户端(Client2)请求过来时,会在locks下创建临时节点 lock2,同样查找locks下的所有临时顺序节点,判断自己的排序是否最小,此时发现 lock1 最小,则进入等待状态
- 同时,客户端(Client2)会在节点 lock1 注册 Watcher 事件,监听 lock1是否还存在
- 监听到lock1不存在了,则lock2尝试获取锁(遍历locks下所有临时顺序节点,判断自己排序是否最小)
- ZK在业务执行完成或发生故障时,会删除临时节点,释放锁
3 三种分布式锁对比
3.1 数据库分布式锁
优点:
- 简单,使用方便,不需要引入 Redis、Zookeeper等中间件
缺点:
- 不适合高并发场景
- db操作性能较差
3.2 Redis分布式锁
优点:
- 性能好,适合高并发场景
- 较轻量级
- 有较好的框架支持,如Redisson
缺点:
- 过期时间不好控制
- 需要考虑锁被其他线程删除的问题
3.3 Zookeeper分布式锁
优点:
- 有较好的性能和可靠性
- 有封装较好的框架,如Curator
缺点:
- 性能不如Redis分布式锁
- 是比较重的分布式锁
4 综合对比
- 从性能角度(从高到低)
Redis > Zookeeper >= 数据库
- 从理解的难易程度角度(从低到高)
数据库 > Redis > Zookeeper
- 从实现的复杂性角度(从低到高)
Zookeeper > Redis > 数据库
- 从可靠性角度(从高到低)
Zookeeper > Redis > 数据库
以上就是常用的分布式锁的3种实现方案
我是【辛勤de小蜜蜂】关注我,我们下期见
-
由于博主才疏学浅,难免会有纰漏,假如您发现了错误或遗漏的地方,还望留言斧正,我会尽快对其加以修正。
-
如果您觉得文章还不错,您的转发、分享、点赞、留言就是对我最大的鼓励。
-
感谢您的阅读,十分欢迎并感谢您的关注。