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。