mysql rwlock_MySQL數據結構分析—讀寫鎖rwlock

目的

在源碼分析mysql多線程操作時,mysql除了使用通常意義上的rwlock,來進行讀寫控制,還使用了一種讀優先的rwlock對元數據鎖(MDL,Meta Data Lock)進行讀寫控制。以下內容中,對mysql的讀寫鎖進行深入的分析,更深刻的了解mysql的讀寫鎖。

數據結構

mysql在多線程處理中,mysql讀寫鎖數據結構mysql_rwlock_t的定義在mysql_thread.h和my_pthread.h頭文件中,具體定義如下所示:

typedef struct st_mysql_rwlock mysql_rwlock_t;

struct st_mysql_rwlock

{

/** The real rwlock */

rw_lock_t m_rwlock;

/** The instrumentation hook.*/

struct PSI_rwlock *m_psi;

};

#define rw_lock_t my_rw_lock_t

/*

On systems which don't support native read/write locks we have

to use own implementation.

*/

typedefstructst_my_rw_lock_t {

pthread_mutex_t lock;          /* lock for structure*/

pthread_cond_t      readers;   /* waiting readers*/

pthread_cond_t      writers;   /* waiting writers*/

intstate;              /* -1:writer,0:free,>0:readers*/

intwaiters;        /* number of waiting writers*/

#ifdefSAFE_MUTEX

pthread_twrite_thread;

#endif

} my_rw_lock_t;

通過以上定義可知,真正的rwlock根據不同的環境,使用不同的處理函數,但是處理的邏輯和思想是是一致的。以上是mysql自身實現的rwlock結構體my_rw_lock_t的定義。從定義可知,該結構定義了兩個條件變量readers、writers,分別用於多線程處理中,讀者和寫者獲取鎖的條件判斷。並且,定義state變量用於標示當前狀態,定義waiters變量用於記錄當前等待的寫者數量。

除此之外,mysql還定義了讀優先的rwlock,定義為pr_lock,用於控制MDL的多線程讀寫。具體定義如下所示:

typedef struct st_mysql_prlock mysql_prlock_t;

struct st_mysql_prlock

{

/** The real prlock */

rw_pr_lock_t m_prlock;

/** The instrumentation hook. */

struct PSI_rwlock *m_psi;

};

typedef struct st_rw_pr_lock_t {

/**

Lock which protects the structure.

Also held for the duration of wr-lock.

*/

pthread_mutex_t lock;

/**

Condition variable which is used to wake-up

writers waiting for readers to go away.

*/

pthread_cond_t no_active_readers;

/** Number of active readers. */

uint active_readers;

/** Number of writers waiting for readers to go away. */

uint writers_waiting_readers;

/** Indicates whether there is an active writer. */

my_bool active_writer;

#ifdef SAFE_MUTEX

/** Thread holding wr-lock (for debug purposes only). */

pthread_t writer_thread;

#endif

} rw_pr_lock_t;

由定義可知,rw_pr_lock_t數據結構定義了一個條件變量no_active_readers,用於當所有讀者處理結束后,喚醒寫者獲取寫鎖。定義active_readers變量標示當前讀者數量,定義writers_waiting_readers變量標示當前等待的寫者數量,定義active_writer變量標示當前是否有寫者獲取了鎖。

源碼實現

根據以上表結構定義,分別對兩種結構的基本操作中獲取讀鎖(rdlock)和寫鎖(wrlock)的實現進行分析和說明。

rwlock實現

首先對rwlock的讀鎖rw_rdlock()的實現my_rw_rdlock()(mysys\thr_rwlock.c:224)進行分析。源碼實現如下所示:

int my_rw_rdlock(my_rw_lock_t *rwp)

{

#ifdef _WIN32

if (have_srwlock)

return srw_rdlock(rwp);

#endif

pthread_mutex_lock(&rwp->lock);

/* active or queued writers */

while (( rwp->state < 0 ) || rwp->waiters)

pthread_cond_wait( &rwp->readers, &rwp->lock);

rwp->state++;

pthread_mutex_unlock(&rwp->lock);

return(0);

}

從以上定義中可知,直到沒有寫者並且無寫者等待時,獲取讀鎖,核心實現為函數中重點標示的部分。

rwlock寫鎖rw_wrlock()實現my_rw_rwlock()(mysys\thr_rwlock.c:264)的源碼如下所示。

int my_rw_wrlock(my_rw_lock_t *rwp)

{

#ifdef _WIN32

if (have_srwlock)

return srw_wrlock(rwp);

#endif

pthread_mutex_lock(&rwp->lock);

rwp->waiters++;                           /* another writer queued */

my_rw_lock_assert_not_write_owner(rwp);

while (rwp->state)

pthread_cond_wait(&rwp->writers, &rwp->lock);

rwp->state = -1;

rwp->waiters--;

#ifdef SAFE_MUTEX

rwp->write_thread= pthread_self();

#endif

pthread_mutex_unlock(&rwp->lock);

return(0);

}

由以上函數實現可知,直到當前沒有讀者時,獲取寫鎖。並且設置為寫者狀態,寫者等待數減少1。核心實現見以上函數中重點標示部分。

從以上實現中可知,讀者和寫者是公平競爭鎖資源的。在mysql的很多場景下,都使用了rwlock,如:權限控制(sql_acl)、插件(sql_plugin)、日志(log)、myisam存儲引擎等子系統。然而,在某些情況下,對讀多寫少的情況下,這種公平競爭鎖資源的實現就顯得不盡人意,如表定義。因此,mysql實現了另一種鎖pr_lock鎖,用於讀優先的多線程控制。

pr_lock實現

根據pr_lock的數據結構定義,對pr_lock的讀鎖rw_pr_rdlock()(mysys\thr_rwlock.c:375)的實現進行分析,源碼如下:

int rw_pr_rdlock(rw_pr_lock_t *rwlock)

{

pthread_mutex_lock(&rwlock->lock);

/*

The fact that we were able to acquire 'lock' mutex means

that there are no active writers and we can acquire rd-lock.

Increment active readers counter to prevent requests for

wr-lock from succeeding and unlock mutex.

*/

rwlock->active_readers++;

pthread_mutex_unlock(&rwlock->lock);

return 0;

}

由以上源碼可知,對於讀優先的鎖來說,可以直接獲取,並且讀者數量增加1。也就是說,讀者可以隨時獲取讀鎖。

pr_lock的寫鎖rw_pr_wrlock()(mysys\thr_rwlock.c:390)的源碼實現如下所示:

int rw_pr_wrlock(rw_pr_lock_t *rwlock)

{

pthread_mutex_lock(&rwlock->lock);

if (rwlock->active_readers != 0)

{

/* There are active readers. We have to wait until they are gone. */

rwlock->writers_waiting_readers++;

while (rwlock->active_readers != 0)

pthread_cond_wait(&rwlock->no_active_readers, &rwlock->lock);

rwlock->writers_waiting_readers--;

}

/*

We own 'lock' mutex so there is no active writers.

Also there are no active readers.

This means that we can grant wr-lock.

Not releasing 'lock' mutex until unlock will block

both requests for rd and wr-locks.

Set 'active_writer' flag to simplify unlock.

Thanks to the fact wr-lock/unlock in the absence of

contention from readers is essentially mutex lock/unlock

with a few simple checks make this rwlock implementation

wr-lock optimized.

*/

rwlock->active_writer= TRUE;

#ifdef SAFE_MUTEX

rwlock->writer_thread= pthread_self();

#endif

return 0;

}

由以上函數可知,直到沒有讀者的情況下,寫者獲取鎖資源。因此,獲取寫鎖時。只有當所有讀鎖都釋放的情況下,寫着才能獲取鎖資源。

從源碼可知,mysql實現的pr_lock主要用於讀優先的情況。通過源碼分析可知,mysql主要用於MDL子系統中。

結論

通過數據結構定義和源碼實現可知,mysql的讀寫鎖不僅有通常的rwlock,用於權限控制(sql_acl)、插件(sql_plugin)、日志(log)、myisam存儲引擎等子系統中。而且實現了pr_lock,用於MDL子系統中。

在基于 rt-thread 的系统中,可以使用 `struct rt_semaphore` 结构体来实现读写锁的控制。具体实现可以参考以下代码: ``` struct rt_semaphore rw_sem; int read_count; /* 初始化读写锁 */ int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { /* 初始化信号量,初始值为 1 */ rt_sem_init(&rw_sem, "rw_sem", 1, RT_IPC_FLAG_FIFO); read_count = 0; return 0; } /* 销毁读写锁 */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { /* 删除信号量 */ rt_sem_delete(&rw_sem); return 0; } /* 对读写锁进行读锁定 */ int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { /* 获取信号量,如果信号量值为 0,则线程阻塞 */ rt_sem_take(&rw_sem, RT_WAITING_FOREVER); read_count++; return 0; } /* 对读写锁进行写锁定 */ int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { /* 获取信号量,如果信号量值为 0,则线程阻塞 */ rt_sem_take(&rw_sem, RT_WAITING_FOREVER); while (read_count > 0) { /* 如果有读线程在使用,则等待读线程全部释放 */ rt_sem_release(&rw_sem); rt_sem_take(&rw_sem, RT_WAITING_FOREVER); } return 0; } /* 对读写锁进行解锁 */ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { /* 释放信号量 */ rt_sem_release(&rw_sem); read_count--; return 0; } ``` 在上述代码中,我们使用了一个信号量来实现读写锁的控制。信号量的初始值为 1,表示读写锁是未锁定状态。当线程需要对读写锁进行锁定时,首先需要获取信号量,如果信号量的值为 0,则线程阻塞等待。对于读锁,只需要将读计数器 `read_count` 加 1 即可;对于写锁,则需要判断是否有读线程在使用,如果有,则等待读线程全部释放后再获取锁。对于解锁操作,只需要释放信号量并将读计数器减 1 即可。 上述代码中的函数名和参数与 POSIX 标准中定义的函数名和参数保持一致,因此可以直接调用这些函数来实现读写锁的控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值