http://www.cnblogs.com/xpchild/p/3789068.html

 

上一篇介绍了MySQL源码中保护内存结构或变量的锁,这里开始介绍下MySQL事务中的表锁。

注1: 在表锁的实现上面,使用【mutex+condition+queue】的结构实现并发,阻塞,唤醒的表锁功能。

注2: 本文进行的一些实验,重要的配置项:

1.  autocommit=0
2.  tx_isolation=read-commited
3.  engine=innodb

1. MySQL加锁的顺序:

这里包括了一个sql在执行的过程中,包括commit,所添加和释放跟事务相关的锁以及加不同锁的加锁顺序,这一篇先重点介绍一下MySQL的表锁。

2. MySQL的表锁

注:通过测试看到,MySQL的表锁,是落入innodb层的代码中实现的。

2.1 重要的数据结构

struct st_thr_lock_info

struct st_thr_lock_data

struct st_thr_lock

通过这三个主要的struct,来实现表锁,这三者之间的关系是:

说明:

1. 每一张表,在打开的时候,创建一个innobase_share对象,并初始化一个st_thr_lock结构进行关联,所有请求这个表的表锁,需要关联这个对象

   2. 每一次sql请求,在open table的过程中,会创建一个table,handler对象,这个handler会初始化一个st_thr_lock_data,关联st_thr_lock结构。

2.2 测试

1. 测试用例:

       session1:        session2:

  lock table pp write;    select * from pp;

  2. 测试的主要步骤:

    1. open table

     2. lock table

3. 表锁相关的case场景:

      1. 加锁

      2. 阻塞

      3. 唤醒 

2.2.1 open table

  open table的细节也可以参考:open table

  这里主要介绍innodb层在open table时创建的主要的数据结构:

  1. innobase_share

innodb层表示一个table的结构,包括初始化一个thr_lock,所有请求表锁的关联结构

  2. ib_table

    innodb层表示一个table的统计信息

  3. handler

    提供给sever层table的结构,所有对innodb层的操作,都通过handler,并初始化了一个st_thr_lock_data

  

主要函数调用栈:

open_table

open_table_from_share

      ha_innobase::open: get_share/thr_lock_init/thr_lock_data_init

        1. 在第一次open 这个表时,创建innobase_share, 初始化THD_LOCK, 初始化 ib_table

        2. 初始化handler,初始化THD_LOCK_DATA。

说明:

  1. 所有的innobase_share结构,保存到一个全局hash表中:innobase_open_tables,全局共享

  2. open结束后,创建完成的所有的相关的数据结构关联图如下:

    

     解释: 红色的部分是表锁的关键结构,mutex用于保护thr_lock queue结构,所有的加锁,wait 锁,线层结构都需要进入thr_lock中的queue,一共有四个queue: read,read_wait, write, write_wait.

      每一个handler都关联thread的一个condition,所有的wait,wakeup,都使用这个condition来完成,这样可以实现定向唤醒,避免广播。

2.2.2 lock table

  session1:请求的lock_type = TL_WRITE

    session2:请求的lock_type = TL_READ

一共的thr_lock_type有14种,分别是:

tl_ignore=-1,
tl_unlock,
tl_read_default,
tl_read,
tl_read_with_shared_locks,
tl_read_high_priority,
tl_read_no_insert,
tl_write_allow_write,
tl_write_concurrent_insert,
tl_write_delayed,
tl_write_default,
tl_write_low_priority,
tl_write,
tl_write_only

主要函数调用栈:

  mysql_lock_tables:把所有table的thr_lock_data放到MYSQL_LOCK的结构中。
    get_lock_data
      ha_innobase::store_lock:把之前的lock清理掉。换成目前要请求的lock类型。

  thr_multi_lock:请求锁表

    thr_lock: 单个thr_lock_data锁表

下面看下关键的三个步骤:锁表,阻塞,唤醒

  锁表:session 1

    session1 使用lock write来加锁,这里加的是排他锁,thr_lock结构上没有其他锁,这里会加成功,

    主要的代码:

       mysql_mutex_lock(&lock->mutex); 锁住这个表的mutex,开始进入这个表锁的串行操作。
lock_type=TL_WRITE
(*lock->write.last)=data; /* Add to running FIFO */
data->prev=lock->write.last;
statistic_increment(locks_immediate,&THR_LOCK_lock); 累计locks_immediate计数

  阻塞:session 2

    session 2申请pp表的read表锁,但session1已经获得的排他锁,这里会阻塞,并wait 这个线程的condition。

    wait_for_lock(wait_queue, data, 0, lock_wait_timeout)
      statistic_increment(locks_waited, &THR_LOCK_lock); 累计locks_waited计数
      (*wait->last)=data; /* Wait for lock */  加入wait队列
      data->prev= wait->last;
      wait->last= &data->next;
      mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout); 等待这个thread的condition

这里的condition要注意,是THD结构中的condition,线程的阻塞,不管是因为什么原因,只需要一个condition就可以完成,没有必要对于不同的锁等待,创建不同的condition。

  唤醒:session 1 unlock。

    session1:使用unlock tables操作。

    /* Unlock lock and free next thread on same lock */
    thr_unlock:
     wake_up_waiters(lock); 唤醒等待同一个lock的thread,这里需要判断lock请求的兼容模式,并且因为使用queue保存了请求wait队列,防止了饿死。
       mysql_cond_signal(cond);

注意:

  对于innodb来说,不论autocommit的设置如何,每一个dml select结束后,都使用thr_unlock释放掉了表锁,这里的理解是:innodb倾向使用行级锁来支持事务,对于保护表metadata信息,则使用MDL来保护,所以innodb对于表锁来说,并没有使用意愿。

后话:

  对于上面的测试,大家可以试一下,先操作session2. 后操作session1。结果是一样的,都是阻塞,但是这里阻塞在什么锁上,完全不同。

简略测试一下:

  session 2: select * from pp :

    open

    获取mdl锁,

    获取表锁,

    执行结束

    释放表锁: (因为autocommit=0,这里并没有释放mdl锁)

  session 1: lock table pp write

    open

    获取mdl排他锁: (阻塞:因为session2没有释放mdl锁,所以这里阻塞)

大家可以看到,这里是因为ddl需要拿到mdl排他锁,而阻塞。

下一篇blog,我们就来看看mdl锁的情况。

    

    

最新文章

  1. ASP.NET Web服务调用发生错误,错误代码404
  2. 【转载】CSS Sticky Footer: 完美的CSS绝对底部
  3. ThinkPHP整合微信支付之发裂变红包
  4. 【Todo】用python进行机器学习数据模拟及逻辑回归实验
  5. computer English
  6. LintCode-Unique Path II
  7. *[codility]MaxDoubleSliceSum
  8. tengine lua 开源一 调用内部接口高效发送文件
  9. leveldb源码笔记
  10. Maven 执行Javadoc时控制台输出乱码问题
  11. PHP 安全检测代码片段
  12. Css3炫酷总结使用
  13. 【最短路】 poj 2387
  14. 获取Excel数据(或部分数据)并导出成txt文本格式
  15. Tomcat、JBOSS、WebSphere、WebLogic、Apache等技术概述
  16. AngularJS进阶(二十八)解决AngualrJS页面刷新导致异常显示问题
  17. Teradata Delete Database and Drop Database
  18. python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
  19. python类型错误:can only concatenate list (not "str") to list
  20. FastAdmin 新年福袋进行中

热门文章

  1. C#控制定位Word光标移动到任意行或者最后一行,取得光标位置等操作
  2. IOS block 记录
  3. 最小化安装CentOS7 + xfce4 +PHP + nginx +mariadb 开发环境
  4. bzoj 1778 [Usaco2010 Hol]Dotp 驱逐猪猡(高斯消元)
  5. 线段和矩形相交 POJ 1410
  6. A题进行时--浙大PAT 1011-1020
  7. HiveContext VS SQLContext
  8. HDU 5833 Zhu and 772002 (高斯消元)
  9. [iOS基础控件 - 6.11.2] - UINavigationController 多控制器 简单使用
  10. mongod的主要参数解释