Chinaunix首页 | 论坛 | 博客
  • 博客访问: 289729
  • 博文数量: 186
  • 博客积分: 1531
  • 博客等级: 上尉
  • 技术积分: 1275
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-06 16:56
文章分类

全部博文(186)

文章存档

2013年(2)

2012年(184)

分类:

2012-11-08 14:12:14

目的

         在源码分析mysql多线程操作时,mysql除了使用通常意义上的rwlock,来进行读写控制,还使用了一种读优先的rwlock对元数据锁(MDLMeta Data Lock)进行读写控制。以下内容中,对mysql的读写锁进行深入的分析,更深刻的了解mysql的读写锁。

数据结构

         mysql在多线程处理中,mysql读写锁数据结构mysql_rwlock_t的定义在mysql_thread.hmy_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.

*/

typedef struct st_my_rw_lock_t {

       pthread_mutex_t lock;          /* lock for structure              */

       pthread_cond_t      readers;   /* waiting readers          */

       pthread_cond_t      writers;   /* waiting writers          */

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

       int             waiters;        /* number of waiting writers      */

#ifdef SAFE_MUTEX

    pthread_t       write_thread;

#endif

} my_rw_lock_t;

 

         通过以上定义可知,真正的rwlock根据不同的环境,使用不同的处理函数,但是处理的逻辑和思想是是一致的。以上是mysql自身实现的rwlock结构体my_rw_lock_t的定义。从定义可知,该结构定义了两个条件变量readerswriters,分别用于多线程处理中,读者和写者获取锁的条件判断。并且,定义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子系统中。

阅读(359) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~