前言
在分布式系统中,我们常常使用锁来保证操作的一致性控制。但是锁的存在则意味着必然存在着锁竞争的情况。而且这种竞争会随着外部请求量的激增而变得更为的激烈。因此我们改进的一个方向是改变锁的粗细力度,从较为简单的粗粒度锁变为更细粒度的锁。细粒度锁相较于粗粒度锁来说,毫无疑问,它能减缓激烈的锁竞争的情情况,但是它在实现上会增加额外的复杂度。这个很好理解,在server端原先只需要维护一把锁就行了,现在则要可能维护一定规模量的小锁。本文笔者来聊聊其中一种细粒度锁的方案实现。
锁的细粒度级别
这里我们首先来了解锁的粗细粒度的级别,对于锁来说,我们可以做到何种粗细粒度的级别呢?
下面我们从粗粒度到细粒度的程度来说,
首先,比较粗粒度的锁,全局单一锁控制,一般做到读写锁分离的程度就够了,特点实现较为的简单直接。但是扩展性不好,容易达到性能瓶颈。
紧接着,稍微细粒度一点的锁,按照partition进行分片的分片锁。这种一个典型的例子是我们将一个大namespace按照规则划分成若干个小namespace,然后给每个namespace分派一个锁做各自namespace下的并发操作的控制。在这种情况下,锁的数量也还是能在一定数量控制之下的。
最后一种,完全意义上的细粒度锁控制,这个粒度甚至可以到ID级别,或者每个数据块Block级别的锁类似这种。到了这个粒度时,锁的数量可以达到成千上万的规模级别,因为它可以具体到最小单位数据粒度级别了。服务端如果此时硬是在内存里存储这些小锁,内存空间也倒是不支持。下面我们就来聊聊这种情况的锁设计实现。
基于小概率锁碰撞的lock pool实现方案
刚刚上面也已经提过,当锁的控制粒度降为每个最小单位数据粒度时。维护全部的小锁并不是一种切实的方案。
那么这个时候怎么办呢?这里推荐的一种方案是lock pool,锁池的方案。这个锁池会维护一定规模的锁。主要关键的一点是,这个锁池内的锁在被释放后,能被其它请求拿去使用,达到锁复用的程度。这里基于的一个背景其实是:
一段时间内,往往只同时存在部分被操作的数据,因此我们实际只需要维护这部分操作需要的锁,而这个数量规模其实是有限的。