1.为什么要分布式锁
在java的分布式中,每个微服务的运行都是独立开的,可能每个微服务都不在一台机器上,如果用本地锁ReentrantLock或者synchronized锁,则无法对某一个资源进行加锁操作,因为这些锁只能锁住当前进程。分布式锁则是实现不同微服务之间进行资源竞争的强有力工具。
2.分布式锁的特征
分布式锁和本地锁要实现的特征一样同时也有一些新的特性,必须要有
1.互斥性:任意时刻只能有一个微服务获得锁,不能同时有两个客户端获得锁
2.安全性:当前微服务获得锁,只能由当前微服务删除锁,其他微服务不能删除
3.可重入:微服务获得锁后,获得锁的微服务可再次申请并获得该锁。
4.容错:当微服务节点宕机或者redis节点宕机后,微服务获得锁后未能释放锁,其他微服务仍然能获得该锁,保证分布式的可用性。
3分布式锁有三种实现方式:
1.基于数据库实现分布式锁;
2.基于缓存(Redis等)实现分布式锁;
3.基于Zookeeper实现分布式锁;
1.基于数据库实现分布式锁
其中通过数据库表建立表字段 lock_key为锁的名称,并设置唯一索引。 在实现中通过线程插入 成功为加锁成功
优点: 实现简单,数据库资源充足,不需要额外中间件
缺点 : 性能比较低,这种方式实现分布式锁时不可重入的,没有容错等待
2.基于缓存(Redis等)实现分布式锁 1 简单实现 基于setnx
这个实现的问题 : redsi 宕机 无法释放锁 其他失败无法等待
进行演变升级
解决办法:设置锁值用一个uuid,每个微服务都不一样,删除的时候判断uuid是否是本微服务的。
代码:
问题:这里又会有一个问题,lock的值获取和删除锁应该是一个原子操作,否则可能会出现lock获取被其他微服务抢占锁,导致删除出错。
解决:使用redis的lua脚本,保证 取值-判断-删除 是原子操作
代码:
问题:设置的过期时间太短,业务代码还没执行完成锁就过期了,锁被其他微服务占了。
解决:设置锁的自动续期。使用redLock算法
Redisson 分布式锁为我们解决了上面的部分问题 整体流程
Redisson分布式锁的优点
1,高可用 性能高,考虑了宕机 超时 等异常场景
Redisson分布式锁的缺点
其实上面那种方案最大的问题,就是如果你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。
但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。 接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。
此时就会导致多个客户端对一个分布式锁完成了加锁。
这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。
可以用RedLock的实现来解决这个问题 不过使用ZK更好
3.基于ZK实现分布式锁
理解节点,为实现分布式锁铺垫。
ZK有四种节点
1、持久节点
默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在。
2、持久节点顺序节点
在创建节点时,zookeeper根据创建的时间顺序给该节点名称进行编号
3、临时节点
和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除
4、临时顺序节点
在创建节点时,zookeeper根据创建的时间顺序给该节点名称进行编号排序(排序的节点形成一个类似队列)。当创建节点的客户端与zookeeper断开连接后,临时节点会被删除(解锁)
加锁流程
client2拿锁,创建临时节点2并排序,临时节点2不在第一位,创建监听器监听前面一个临时节点1,拿锁失败==监听中
ZK分布式锁优点
1高可用 集群部署 2一致性强 因为zk是cp 模型 创建数据会先保证写入
ZK分布式锁缺点
1,并发性能,因为zk只有主节点写入 从节点读 2,选举不可用
小总结:一致性要求高 用zk 性能要求高,可忍受使用
未来发展
1. etcd简介
简介 Etcd是CoreOS基于Raft协议开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。
在分布式系统中,如何管理节点间的状态一直是一个难题,etcd像是专门为集群环境的服务发现和注册而涉及,它提供了数据TTL失效 、数据改变监视、多值、目录监听、分布式锁原子操作等功能, 可以方便的跟踪并管理集群节点的状态。
特点
简单:curl可访问的用户的API(HTTP + JSON)
安全:可选的SSL客户端证书认证
快速: 单实例每秒1000次写操作
可靠:使用Raft算法保证一致性
主要功能
1. 基本的key-value存储
2. 监听机制
3. key的过期及续约机制, 用于监控和服务发现
4. 原子Compare And Swap和Compare And Delete, 用于分布式锁和leader选举
因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。
保持独占即所有获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时,只有一个成功。而创建成功的用户就可以认为是获得了锁。
控制时序,即所有想要获得锁的用户都会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。