不可否认,在多核时代,多线程并发是常态, 分布式锁服务通过持续心跳来保证锁的健壮性,让用户不用投入很多精力关注丢锁,但也有可能异常的用户进程持续占据锁。
我们也曾经遇到过该类场景,因为机器 load 高,硬件问题等原因,设备上的进程假死但其中仍存在部分线程在工作,比如锁的心跳维护线程依然正常运行,这样锁节点仍然是被假死进程占据着,且无法被其他进程抢占。针对该场景,为了保证锁最终可以被调度,提供了可以安全释放锁的会话加黑机制。
当用户需要将发生假死的进程持有的锁释放时,可以通过查询会话信息,并将会话加黑,此后,心跳将不能正常维护,最终导致会话过期,锁节点被安全释放。这里我们不是强制删除锁,而是选用禁用心跳的原因如下:
a. 删除锁操作本身不安全,如果锁已经被其他人正常抢占,此时删锁请求会产生误删除。
b. 删除锁后,持有锁的人会话依然正常,它仍然认为自己持有锁,会打破锁的互斥性原则。
切换效率:
当进程持有的锁需要被重新调度时,持有者可以主动删除锁节点,但当持有者发生异常(如进程重启,机器宕机等),新的进程要重新抢占,就需要等待原先的会话过期后,才有机会抢占成功。默认情况下,分布式锁使用的会话生命期为 50 秒,当持有锁的进程意外退出后(未主动释放锁),最长需要经过 50 秒锁节点才可以被再次抢占。
要提升切换精度,本质上要压缩会话生命周期,同时也意味着更快的心跳频率,对后端更大的访问压力。我们通过对后端进行优化,使得会话周期可以进一步压缩,提升锁的切换效率。
例如:目前给 TableStore 提供 30 秒的分布式锁,同时也通过提供 Restful 接入方式,让用户可以自定义会话时长及控制心跳发送,同时我们会将锁的精度进一步提高到 20 秒之内 。
结合具体的业务场景,例如守护进程发现锁持有进程挂掉的场景,提供锁的 CAS 释放操作,使得进程可以零等待进行抢锁。比如利用在锁节点中存放进程的唯一标识,强制释放已经不再使用的锁,并重新争抢,该方式可以彻底避免进程升级或意外重启后抢锁需要的等待时间。