MySQL锁系列3 MDL锁
http://www.cnblogs.com/xpchild/p/3790139.html
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 };


enum enum_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 };


enum enum_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和事务隔离级别,改动会比较大。
MySQL锁系列3 MDL锁的更多相关文章
- MySQL锁系列2 表锁
http://www.cnblogs.com/xpchild/p/3789068.html 上一篇介绍了MySQL源码中保护内存结构或变量的锁,这里开始介绍下MySQL事务中的表锁. 注1: 在表 ...
- mysql 原理 ~ DDL之mdl锁
一 简介: MDL锁 二 具体 1 MDL锁 1 增删查改 申请MDL读锁 2 ddl语句 1. 拿MDL写锁 2. 降级成MDL读锁 3. 真正做DDL ...
- Innodb 锁系列2 事务锁
上一篇介绍了Innodb的同步机制锁:Innodb锁系列1 这一篇介绍一下Innodb的事务锁,只所以称为事务锁,是因为Innodb为实现事务的ACID特性,而添加的表锁或者行级锁. 这一部分分两篇来 ...
- 【MySQL 读书笔记】全局锁 | 表锁 | 行锁
全局锁 全局锁是针对数据库实例的直接加锁,MySQL 提供了一个加全局锁的方法, Flush tables with read lock 可以使用锁将整个表的增删改操作都锁上其中包括 ddl 语句,只 ...
- 有了MDL锁视图,业务死锁从此一目了然
摘要:MDL锁视图让一线运维人员清晰地查看数据库各session持有和等待的元数据锁信息,从而找出数据库MDL锁等待的根因,准确地进行下一步决策. 当多用户共同存取数据时,数据库中就会产生多个事务同时 ...
- MySQL锁系列1
http://www.cnblogs.com/xpchild/p/3782311.html MySQL的锁:MySQL内部有很多种类的锁,按照用途不同,可以分为两类:1. 保护内存结构的锁,实现同 ...
- Mysql高手系列 - 第26篇:聊聊如何使用mysql实现分布式锁
Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 欢迎大家加我微信itsoku一起交流java.算法.数据库相关技术. 这是Mysql系列第26篇. 本篇我们使用my ...
- mysql小白系列_07 锁与事务
1.MySQL参数autocommit生产环境设1还是0?为什么? 2.MySQL参数tx_isolation生产环境上大多数是设什么值,为什么? 3.与MySQL锁相关的有哪些因素? 1.MySQL ...
- mysql 开发进阶篇系列 13 锁问题(关于表锁,死锁示例,锁等待设置)
一. 什么时候使用表锁 对于INNODB表,在绝大部分情况下都应该使用行锁.在个别特殊事务中,可以考虑使用表锁(建议). 1. 事务需要更新大部份或全部数据,表又比较大,默认的行锁不仅使这个事务执行效 ...
随机推荐
- Android开源项目SlidingMenu深切解析
demo:http://download.csdn.net/detail/javadxz/6954819 SlidingMenu的是一种斗劲新的设置界面或设备界面结果,在主界面左滑或者右滑呈现设置界面 ...
- Android应用启动时间及启动日志获取方法
1. Android应用中,可以使用如下方式进行应用启动时间的查看 2. 启动日志获取方法:
- OpenGL超级宝典第5版&&GLSL法线变换
在GLSL中,有一些情况需要把局部坐标系下的向量或点转换到视点坐标系下,如光照计算时,需要把法向转化到视点坐标系.如果是模型上一点p 转化到视点坐标系下,直接(model-view matrix )* ...
- 试验Windows Embedded Standard 7 Service Pack 1 Evaluation Edition
=========================================== 是否支持再使用 RT 7 Lite 精简 ? ================================= ...
- cocos2d-x 详解之 CCTexture2D(纹理图片)和 CCTextureCache(纹理缓存)
精灵和动画都涉及到纹理图片的使用,所以在研究精灵与动画之前,我们先来了解一下纹理图片类CCTexture2D和纹理缓存CCTextureCache的原理: 当一张图片被加载到内存后,它是以纹理的形式存 ...
- offsetWidth、offsetleft 等图文详解
网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...
- 简易nagios安装
这段时间一直在进行nagios安装的实验,进行了很多的实验,现在也就是将这些进行一些基础的记录. 本篇主要讲述的是进行nagios的简易安装,在安装完成之后,能够在web页面上看到本地的监控图像, n ...
- 关于javascript模式一书中var white = new Array(256).join(“ ”)
直接进入正题 var white = new Array(256).join(" ") 运行后,我们会发现white.length的长度是255,这个是为什么呢?书上没有给出解答, ...
- java类加载与初始化
第一段: class A{ public A(){ this.list(); } public void list(){ System.out.println("in a list..&qu ...
- AS3 条件编译
转:http://www.lite3.cn/blog/?p=404&cpage=1 知道C/C++ 有条件编译,一直想着AS要是也有条件编译就好了,今天搜了半天Google,终于让我找到了 下 ...