分布式锁
在单机系统中,如果多线程对一个共享资源访问时,可以用锁处理。
在分布式系统中,多个服务对同一个共享的资源访问时,例如同一用户对于某个接口未返回时不能重复调用,由于服务不在同一机器或不能共享内存,不能共享之前的锁,即自己的锁只对自己有效并不影响别的 ,因此需要一个可以分布式锁的能力。
分布式锁能力有几种解决方案:
- 通过数据库实现分布式锁
- 基于缓存Redis实现分布式锁
- 基于Zookeeper实现分布式锁
1. 通过数据库实现分布式锁
具体的实现方法可以在数据库中创建一个表,包括调用方法名、参数、调用者、调用时间等字段,在调用时在不表中插入数据,表示成功获取锁,释放锁时删除记录。
该种方式实现起来不复杂,但是有一些缺点:
- 分布式锁的性能受制于数据库的性能;
- 没有锁失效机制,如果某次成功插入数据后,服务器宕机,导致锁一直没有释放
- 不具备锁阻塞特性,如果获取锁失败,需要制定获取逻辑,例如循环获取
2. 基于redis实现
redis实现分布式锁的方式就是,通过插入key-value,当key不存在时,增加k-v表示成功获取锁,如果k存在,表示获取失败。
其优点是,redis基于内存存储性能较高,而且redis支持set k-v时附带超时时间,即锁时间到期会自动释放,避免死锁相关问题
但redis同样不具备锁阻塞特性。
3. 基于Zookeeper
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。
- 创建一个目录mylock;
- 线程A想获取锁就在mylock目录下创建临时顺序节点;
- 获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
- 线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
- 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
Zookeeper具备高可用、可重入、阻塞锁特性。