InnoDB 读写锁rw_lock_t实现

10月忙得像shi一样,不管怎样,忙里偷闲在这个月结束之前写上一篇,介绍InnoDB读写锁的实现

InnoDB读写锁特点

1. 读与读之间不冲突,读与写,写与写之间冲突,这个是读写锁最基本的特征
2. 申请写锁时,如果锁被读持有,将发生写锁等待,该写锁之后的所有读锁会等待该写锁成功获取后才能申请成功,这保证了公平:写锁请求不会被饿死

基本数据结构

sync_array_struct: 保存用于线程同步结构的数组,数组成员类型为sync_cell_struct
sync_cell_struct: 一个线程同步结构体,部分代码:

struct sync_cell_struct {
	void*		wait_object;	/*!< pointer to the object the
					thread is waiting for; if NULL
					the cell is free for use */
	ulint		request_type;	/*!< lock type requested on the
					object */
	os_thread_id_t	thread;		/*!< thread id of this waiting
					thread */
	ibool		waiting;	/*!< TRUE if the thread has already
					called sync_array_event_wait
					on this cell */
	ib_int64_t	signal_count;	/*!< We capture the signal_count
					of the wait_object when we
					reset the event. This value is
					then passed on to os_event_wait
					and we wait only if the event
					has not been signalled in the
					period between the reset and
					wait call. */
	time_t		reservation_time;/*!< time when the thread reserved
					the wait cell */
};

以上是部分成员,这个结构体与srv_slot_struct,srv_conc_slot_struct作用基本相同,其中包含一个同步事件:wati_object,其类型可以是:rw_lock_t和mutext_t,其内部都有一个os_event_t类型的成员event,可以对其执行set/reset/wait,想了解其具体实现,请参考代码

rw_lock_struct:读写锁结构体,部分成员:

typedef struct rw_lock_struct		rw_lock_t;
struct rw_lock_struct {
	volatile lint	lock_word;
				/*!< Holds the state of the lock. */
	volatile ulint	waiters;/*!< 1: there are waiters */
	volatile os_thread_id_t	writer_thread;
				/*!< Thread id of writer thread. Is only
				guaranteed to have sane and non-stale
				value iff recursive flag is set. */
	os_event_t	event;	/*!< Used by sync0arr.c for thread queueing */
	os_event_t	wait_ex_event;
				/*!< Event for next-writer to wait on. A thread
				must decrement lock_word before waiting. */

有两个os_event_t类型的成员:event和wait_ex_eveint,其中wait_ex_event的作用是:如果当前锁只被读者持有,之后第一个写锁请求将在这个事件上等待,当这些读者中最后一个unlock时,会唤醒等待这个事件上的写等待,其它申请锁时冲突的情况将在event上发生所等待,这个事件在写者unlock时去唤醒

神奇的lock_word
InnoDB实现公平读写锁是靠这个成员来保证的,它被初始化为:X_LOCK_DECR

/* We decrement lock_word by this amount for each x_lock. It is also the
start value for the lock_word, meaning that it limits the maximum number
of concurrent read locks before the rw_lock breaks. The current value of
0x00100000 allows 1,048,575 concurrent readers and 2047 recursive writers.*/
#define X_LOCK_DECR		0x00100000

rw_lock_struct支持recursive模式,本文不打算介绍这个,从注释中可以看出X_LOCK_DECR被初始为一个较大的值,那么它代表什么意思呢?
1. 成功申请一个读锁,lock_word减1,释放时加1
2. 成功申请一个写锁,lock_word减X_LOCK_DECR,释放时加X_LOCK_DECR
根据以上行为,lock_word的取值区间有:(recursive writers模式不包含在内)
1. lock_word = X_LOCK_DECR: 锁没有被持有
2. 0 < lock_word < X_LOCK_DECR: 锁上有X_LOCK_DECR-lock_word个读者 3. lock_word = 0: 锁上有一个写者 4. -X_LOCK_DECR < lock_word < 0: 锁被读者持有,但是上面存在一个写申请 原理: 对lock_word的操作被保证是原子操作,且只有当lock > 0 时才允许加读锁或者写锁,当锁上有X_LOCK_DECR-lock_word个读者时(情况2),之后第一个写请求会原子地将lock_word减去X_LOCK_DECR(情况3),这保证之后来到的读请求不会在该写锁被获取之前进入

LOCK/UNLOCK操作

rw_lock_x_lock_func
1. 原子操作(如果lock_word > 0, 将其减去X_LOCK_DECR),之后spin,等待lock_word=0,spin次数超过一定次数后,从sync_primary_wait_array中找到一个sync_cell,将其event指向rw_lock_struct中的wait_ex_event,等待被唤醒,满足lock_word=0时退出
2. lock_word <= 0(已经存在其他写锁请求),进入spin,等待lock_word >0, 之后从sync_primary_wait_array中找到一个sync_cell,将其event指向rw_lock_struct中的event,等待被唤醒,唤醒后,进入1中逻辑执行

rw_lock_x_unlock_func
1. 原子操作(将lock_word增加X_LOCK_DECR),唤醒event上等待的事件

rw_lock_s_lock_func
1. 原子操作(如果lock_word > 0, 将其减去1后返回),如果lock_word > 0,加锁成功,返回
2. lock_word <= 0,等待lock_word > 0,spin一定次数后,从sync_primary_wait_array中找到一个sync_cell,将其event指向rw_lock_struct中的event,等待被唤醒

rw_lock_s_unlock_func
原子操作(将lock_word增加1),如果lock_word为0,说明存在写锁等待,唤醒wait_ex_event

总结

1. 写锁申请由于当时有读者(lock_word > 0)无法申请成功时,将lock_word减成负数,在wait_ex_event上等待被唤醒
2. 写锁申请由于当时有写锁/写等待(lock_word <= 0)无法申请成功时,将在event上等待被唤醒,被唤醒后将最终进入1中逻辑 3. 最后一个读锁释放时,会唤醒wait_ex_event 4. 写锁释放时,会唤醒event

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注