MySQL为了保护数据字典元数据,使用了metadata lock,即MDL锁,保证在并发的情况下,结构变更的一致性。

MDL锁的加锁模式和源码上的组织上和上一篇blog中MySQL表锁的实现方式一致,都采用了【mutex+condition+queue】来实现并发,阻塞,唤醒的控制。

下面就来看看MDL锁:

1. 重要的数据结构:

1. MDL_map

mdl_map使用hash表,保存了MySQL所有的mdl_lock,全局共享,使用MDL_KEY作为key来表,key=【db_name+table_name】唯一定位一个表。

2. mdl_context

mdl_context在MySQL为每一个connection创建thd时,初始化一个mdl上下文,保存了当前session请求的mdl信息。

3. MDL_lock

mdl_lock表示系统的一个mdl锁,所有的mdl request都请求对应的mdl_lock,这个mdl_lock结构保存了两个queue,一个是grant_queue表示拿到lock的请求队列。

一个是wait_queue表示请求这个mdl_lock的阻塞队列。

4. MDL_wait

mdl_wait包装了一个mutex和一个condition,提供了所有的加锁,wait,notify操作。

5. MDL_request

在open table的时候,会init一个request,包含了请求的enum_mdl_type,enum_mdl_duration,MDL_ticket,MDL_key。

下面再看看三个重要的枚举类型:

enum enum_mdl_namespace { GLOBAL=0,SCHEMA,TABLE,FUNCTION,PROCEDURE,TRIGGER,

EVENT,COMMIT,/*This should be the last !*/NAMESPACE_END };

enumenum_mdl_duration {/**

Locks with statement duration are automatically released at the end

of statement or transaction.*/MDL_STATEMENT= 0,/**

Locks with transaction duration are automatically released at the end

of transaction.*/MDL_TRANSACTION,/**

Locks with explicit duration survive the end of statement and transaction.

They have to be released explicitly by calling MDL_context::release_lock().*/MDL_EXPLICIT,/*This should be the last !*/MDL_DURATION_END };

enumenum_mdl_type {

MDL_INTENTION_EXCLUSIVE= 0,

MDL_SHARED,

MDL_SHARED_HIGH_PRIO,

MDL_SHARED_READ,

MDL_SHARED_WRITE,

MDL_SHARED_NO_WRITE,

MDL_SHARED_NO_READ_WRITE,

MDL_EXCLUSIVE,

MDL_TYPE_END};

首先:enum_mdl_namespace 表示mdl_request的作用域,比如alter table操作,需要获取TABLE作用域。

然后:enum_mdl_duration 表示mdl_request的持久类型,比如alter table操作,类型是MDL_STATEMENT,即语句结束,就释放mdl锁。又比如autocommit=0;select 操作,类型是MDL_TRANSACTION,必须在显示的commit,才释放mdl锁。

最后:enum_mdl_type 表示mdl_request的lock类型,根据这个枚举类型,来判断是否兼容和互斥。

2. 测试

下面根据一个测试,看一下加锁,释放,阻塞的过程,已经主要的函数调用栈:

session1:            session2:

set autocommit=0;        alter table pp add name varchar(100):

select * from pp;

2.1 创建connection过程中,初始化mdl_context.

函数调用:

handle_connections_sockets

MDL_context::init: 每一个connection对应一个mdl_context

2.2 初始化mdl_request

函数调用:

parse_sql

st_select_lex::add_table_to_list

MDL_request::init

说明: 在session1的过程中,创建的mdl_request:

mdl_namespace=MDL_key::TABLE,

db_arg=0x8c7047c8 "xpchild",

name_arg=0x8c7047d0 "pp",

mdl_type_arg=MDL_SHARED_READ,

mdl_duration_arg=MDL_TRANSACTION

2.3 加锁

acquire_lock:

if (lock->can_grant_lock(mdl_request->type, this))

{lock->m_granted.add_ticket(ticket);

mysql_prlock_unlock(&lock->m_rwlock);

m_tickets[mdl_request->duration].push_front(ticket);

mdl_request->ticket=ticket;

}

说明:首先进行兼容性判断,如果兼容,那么就把ticket加入到队列中,加锁成功。

函数调用栈

open_and_lock_tables

open_table

1. 排他锁使用

lock_table_names

MDL_context::acquire_locks

2. 共享锁使用

open_table_get_mdl_lock

MDL_context::try_acquire_lock

2.4 阻塞

下面进入session2. 因为session1拿到了pp表的share读锁,但session2的alter操作的mdl_request类型是:MDL_INTENTION_EXCLUSIVE,兼容性判断是互斥,所以ddl被阻塞。

while (!m_wait_status && !thd_killed(thd) &&wait_result!= ETIMEDOUT && wait_result !=ETIME)

{

wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,abs_timeout);

}

说明:上面的这段代码,session2进入阻塞状态,等待超时或者mdl_wait中的条件变量。

2.5 唤醒

session1进行提交动作,commit。 然后session1 release mdl_lock,最后wake up session2.  session 2完成alte操作。

MDL_context::release_lock();lock->remove_ticket();

reschedule_waiters();while ((ticket= it++))

{if (can_grant_lock(ticket->get_type(), ticket->get_ctx()))

{if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))

MDL_wait::set_status();

mysql_cond_signal(&m_COND_wait_status);

说明: commit操作,释放session 1持有的mdl事务锁,然后遍历wait队列,判断兼容性测试,最后wakeup session2.

总结: 根据上面的测试,我们看到,mdl的机制和表锁的机制基本一致性,但从上面的测试和源码的设计上,也看到MySQL表锁,mdl锁令人蛋疼的地方。

3. 蛋疼的锁

下面简单介绍下MySQL锁令人蛋疼的两个地方:

1. 事务开始begin transaction的位置

MySQL的设计:在设置的autocommit=0;read_commited的时候,无论session的第一条语句是select还是dml,都开始一个事务,然后直到commit,所持有的MDL锁也一直维持到commit结束。

Oracle的设计:在session的第一条更新语句发起时,才创建transaction,在读多的系统上,减少了阻塞的发生可能性。特别是在开发人员发起select语句时,认为没有更新,就不再commit。但在MySQL上,发起select语句,而忘记commit,是非常危险的。

2. ddl语句阻塞

MySQL的设计:ddl语句发起时,如果无法获取排他锁,那么ddl将进入阻塞状态,但由于是queue的设计,就阻塞了后续所有的dml和selec操作,在高并发系统上,可能会引起雪崩。

Oracle的设计:在oracle 11g之前,ddl语句是fast fail的,不进入阻塞状态,所以繁忙的表进行ddl操作时,经常遇到的错误:ORA-00054: resource busy。但在11g之后虽然可以进行阻塞,并提供了ddl_time_out这样的参数进行控制,但在高并发的系统上,运维的操作依然不采用,而是fast fail。

后话:

这里可以参照oracle的设计进行改良,ddl语句阻塞相对改源码来说,比较简单。而事务开始的位置,牵涉到mvcc和事务隔离级别,改动会比较大。

下一篇blog介绍下innodb的锁。

mysql mdl 锁_MySQL锁系列3 MDL锁相关推荐

  1. MySQL mdl导入_MySQL源码学习——MDL字典锁

    什么是MDL MDL,Meta Data lock,元数据锁,一般称为字典锁.字典锁与数据锁相对应.字典锁是为了保护数据对象被改变,一般是一些DDL会对字典对象改变,如两个TX,TX1先查询表,然后T ...

  2. mysql设置乐观锁_mysql数据库怎么设置乐观锁

    乐观锁与悲观锁不同的是,它是一种逻辑上的锁,而不需要数据库提供锁机制来支持当数据很重要, 回滚或重试一次需要很大的开销时,需要保证操作的ACID性质, 此时应该采用悲观锁而当数据对即时的一致性要求不高 ...

  3. mysql常见死锁_MySQL死锁系列-常见加锁场景分析

    如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们会首先讲解一下隔离等级.不同 SQL 语句 和 当前数据库数据对 ...

  4. mysql where从句_MySQL死锁系列-常见加锁场景分析

    如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们会首先讲解一下隔离等级.不同 SQL 语句 和 当前数据库数据对 ...

  5. mysql死锁影响_MySQL死锁系列-常见加锁场景分析

    各位看官内容喜欢的话,动动手指点个 ,点个关注呗!!谢谢支持! 如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们 ...

  6. mysql ddl 锁_MySQL Online DDL导致全局锁表案例分析

    MySQL Online DDL导致全局锁表案例分析 我这边遇到了什么问题? 线上给某个表执行新增索引SQL, 然后整个数据CPU打到100%, 连接数暴增到极限, 最后导致所有访问数据库的应用都奔溃 ...

  7. mysql innodb 间隙锁_MySQL中InnoDB的间隙锁问题

    在为一个客户排除死锁问题时我遇到了一个有趣的包括InnoDB间隙锁的情形.对于一个WHERE子句不匹配任何行的非插入的写操作中,我预期事务应该不会有锁,但我错了.让我们看一下这张表及示例UPDATE. ...

  8. mysql myisam表_mysql优化之MyISAM表锁

    概述 MyISAM存储引擎只支持表锁,mysql的表锁有两种模式:读锁和写锁. 他们的兼容关系是(对myisam的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写操作*)和(对myisa ...

  9. mysql版本号锁_MySQL使用版本号实现乐观锁

    乐观锁适用于读多写少的应用场景 乐观锁Version图示 Project Directory Maven Dependency 1 <?xml version="1.0" e ...

  10. mysql事务隔离级别与锁_mysql事务隔离级别与锁

    2.隔离级别实现 上一节介绍了ANSI定义的3种异象,及根据禁止异象的个数而定义的事务隔离级别.因为不存在严格.严谨的"官方"定义,各主流 2.1 Lock-based 隔离级别实 ...

最新文章

  1. Qcon大会归来(r12笔记第36天)
  2. xampp的Apache配置
  3. NLP学习笔记:word2vec
  4. (转)交换机攻击方法描述
  5. [导入]MsAjax Lib- Boolean 类型扩展
  6. java String.replaceAll中特殊字符问题
  7. dotenv 是什么 怎么使用
  8. UReport2初体验
  9. 汇编语言编译器 masm.exe and link.exe
  10. echart的基本使用方法
  11. 别错过,卡方检验实用总结!
  12. 恒指期货高手背后的辛酸付出谁又能懂
  13. 2022年最好的游戏引擎是什么?
  14. python3今日头条App电商数据抓取
  15. Android studio 回退按钮显示
  16. 二进制包方式部署k8s集群
  17. 人工智能换脸python_AI换脸(手把手教你实现吴彦祖变苏大强)
  18. 学生托管班_托管班一般多少钱一个月
  19. C# WINFORM 主窗口把数据传给子窗口,编辑后再返回主窗口
  20. 基于惠斯顿电桥的压力传感器的解决方案

热门文章

  1. 【Electronics】基于锁相环CD4046的数字频率合成器
  2. Cherry的【PRINCE2】入门学习笔记
  3. 老徐WEB:js入门学习 - javascript对象之String对象
  4. 网格设计版式设计_设计师指南,使版式更加充实
  5. 腾讯视频怎么录屏,腾讯视频怎么截长视频
  6. 大数据用户画像之基本概念(一)
  7. Java正则从一段字符串中取email地址
  8. 安卓8.0 -x86虚拟机支持arm
  9. 苹果cms10好看的模板苹果cmsV10响应式视频影视电影网站源码
  10. 汇编语言小设计——抢答器