分布式锁是指分布式环境下,系统部署在多个机器中,实现多进程分布式互斥的一种锁。实现分布式锁有三种主流方式,接下来一一盘点。
盘点之前要说说选择时的优缺点
数据库实现的锁表完全不推荐。
Redis分布式锁性能优于ZooKeeper,因为不需要反复创建节点。
实现的简单程度和可靠性ZooKeeper更好,因为其封装好的框架几乎能应对所有的分布式锁问题。
基于数据库实现的——锁表
实际上就是开一个表。当我们要锁住某个资源时,就在该表中增加一条记录,想要释放锁的时候就删除这条记录。但数据库表要磁盘io,所以效率很低。并且一旦这个数据库寄了,那系统崩了。数据库锁没有失效时间,未获得锁的进程只能一直等待已获得锁的进程主动释放锁。倘若已获得共享资源访问权限的进程突然挂掉、或者解锁操作失败,使得锁记录一直存在数据库中,无法被删除,而其他进程也无法获得锁,从而产生死锁现象。
基于缓存实现的——缓存锁
就是说把数据存放在计算机内存中,不需要写入磁盘,减少了IO读写,实现时候是维护一个队列来维持进程访问共享资源的顺序的。
Redis的setnx和delete方法就支持这一方式。
也存在缺陷,因为进程没法主动释放锁,假设ABC三个进程来,A完事了到B,B进程对应的服务器坏了,那C就得等B超时自动释放才能访问共享资源。通过超时时间来控制锁的失效时间,并不是十分靠谱,因为一个进程执行时间可能比较长,或受系统进程做内存回收等影响,导致时间超时,从而不正确地释放了锁。
ZooKeeper
ZooKeeper基于树形数据存储结构实现分布式锁,ZooKeeper的树形数据存储结构主要由4种节点构成:
- 持久节点(PERSISTENT)。这是默认的节点类型,一直存在于ZooKeeper中。
- 持久顺序节点(PERSISTENT_SEQUENTIAL)。在创建节点时,ZooKeeper根据节点创建的时间顺序对节点进行编号命名。
- 临时节点(EPHEMERAL)。当客户端与Zookeeper连接时临时创建的节点。与持久节点不同,当客户端与ZooKeeper断开连接后,该进程创建的临时节点就会被删除。
- 临时顺序节点(EPHEMERAL_SEQUENTIAL)。就是按时间顺序编号的临时节点。
ZooKeeper是根据临时顺序节点来实现的分布式锁。以电商售卖吹风机的场景为例。假设用户A、B、C同时在11月11日的零点整提交了购买吹风机的请求,ZooKeeper会为每个进程分配一个临时顺序节点。每个节点都有一个监听器监听当前自己是不是序号最小的**(理论上要监听共享资源上的所有节点,实际上只要监听自己上一个节点的状态就行)**,是就去访问共享资源,不是就判断:
- 假设一个进程要读,前面有写的话,就得等待;假设前面没有写的,就可以并发读。
- 假设一个进程要写,前面有进程在就得等待。