正文
注:ZAB 一致性协议保证了锁数据的安全性,不会因为数据丢失造成多个锁持有者;心跳保活机制解决死锁问题,防止由于进程挂掉或者僵死导致的锁长时间被无效占用。具备阻塞锁特性,并通过 Watch 机制能够及时从阻塞状态被唤醒。
解锁流程是
删除当前线程创建的临时接点。
该方案的问题在于通过心跳保活机制解决死锁会造成锁的不安全性,可能会出现如下场景:
持有锁的线程 A 僵死或网络故障,导致服务端长时间收不到来自客户端的保活心跳,服务端认为客户端进程不存活主动释放锁,线程 B 抢到锁,线程 A 恢复,同时有两个线程访问共享资源。
基于上诉对现有锁方案的讨论,我们能看到,一个理想的锁设计目标主要应该解决如下问题:
-
锁数据本身的安全性。
-
不发生死锁。
-
不会有多个线程同时持有相同的锁。
而为了实现不发生死锁的目标,又需要引入一种机制,当持有锁的进程因为宕机、GC 活者网络故障等各种原因无法主动过释放锁时,能够有其他手段释放掉锁,主流的做法有两种:
-
锁设置过期时间,过期之后 Server 端自动释放锁。
-
对锁的持有进程进行探活,发现持锁进程不存活时 Server 端自动释放。
实际上不管采用哪种方式,都可能造成锁的安全性被破坏,导致多个线程同时持有同一把锁的情况出现。因此我们认为锁设计方案应在预防死锁和锁的安全性上取得平衡,没有一种方案能够绝对意义上保证不发生死锁并且是安全的。
而锁一般的用途又可以分为两种,实际应用场景下,需要根据具体需求出发,权衡各种因素,选择合适的锁服务实现模型。无论选择哪一种模型,需要我们清楚地知道它在安全性上有哪些不足,以及它会带来什么后果。
-
为了效率,主要是避免一件事被重复的做多次,用于节省 IT 成本,即使锁偶然失效,也不会造成数据错误,该种情况首要考虑的是如何防止死锁。
-
为了正确性,在任何情况下都要保证共享资源的互斥访问,一旦发生就意味着数据可能不一致,造成严重的后果,该种情况首要考虑的是如何保证锁的安全。
下面主要介绍一下 SharkLock 的一些设计选择。
锁信息设计如下