9.1.3 锁
为了实现并发控制,OceanBase需要对一行记录加共享锁或者互斥锁。为此,专门实现了QLock,代码如下:
struct QLock
{
enum State
{
EXCLUSIVE_BIT=1UL<<31,
UID_MASK=~EXCLUSIVE_BIT
};
volatile uint32t n_ref;//表示持有共享锁的引用计数
volatile uint32t uid;//表示持有互斥锁的用户编号
//加共享锁,uid为用户编号,end_time为超时时间
int shared_lock(const uint32_t uid,const int64_t end_time=-1);
//解除共享锁
int shared_unlock();
//加互斥锁,uid为用户编号,end_time为超时时间
int exclusive_lock(const uint32_t uid,const int64_t end_time=-1);
//解除互斥锁
int exclusive_unlock(const uint32_t uid);
//共享锁升级为互斥锁,uid为用户编号,end_time为超时时间
int share2exclusive_lock(const uint32_t uid,const int64_t end_time=-1);
//互斥锁降级为共享锁
int exclusive2shared_lock(const uint32_t uid);
};
在QLock的实现中,每把锁占用8个字节,其中4个字节为nref,表示持有共享锁的引用计数,另外4个字节为uid,表示持有互斥锁的用户编号(例如线程编号)。uid的最高位(EXCLUSIVE_BIT)表示是否为互斥锁,其余31位表示用户编号。
sharelock用于加共享锁,实现时只需要将n_ref原子加1;exclusive_lock用于加互斥锁,实现时需要将EXCLUSIVE_BIT置1并等待持有共享锁的所有用户解锁完成。另外,为了避免新用户不断产生并持有共享锁导致无法获取互斥锁的情况,exclusive_lock实现步骤如下:
1)将EXCLUSIVE_BIT置为1;
2)等待持有共享锁的所有用户解锁完成;
3)如果第2)步无法在超时时间内完成,加锁失败,将EXCLUSIVE_BIT重新置为0。
第1)步执行完成后,新产生的用户无法获取共享锁。这样,只需要等待已经持有共享锁的用户解锁即可,不会出现获取互斥锁时“饿死”的现象。
share2exclusive_lock将共享锁升级为互斥锁,实现时首先升级为互斥锁,如果获取成功,接着再解除共享锁,即引用计数减1。