06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?
06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?
讲述:林晓斌
时长13:02大小5.97M
全局锁
表级锁
小结
上期问题时间
赞 250
提建议
精选留言(416)
- echo_陈置顶2018-11-26mysql 5.6不是支持online ddl了吗?也就是对表操作增加字段等功能,实际上不会阻塞读写?
作者回复: Online DDL的过程是这样的: 1. 拿MDL写锁 2. 降级成MDL读锁 3. 真正做DDL 4. 升级成MDL写锁 5. 释放MDL锁 1、2、4、5如果没有锁冲突,执行时间非常短。第3步占用了DDL绝大部分时间,这期间这个表可以正常读写数据,是因此称为“online ” 我们文中的例子,是在第一步就堵住了
共 68 条评论483 - 哈哈哈置顶2019-01-24MDL作用是防止DDL和DML并发的冲突,个人感觉应该写清楚,一开始理解为select和update之间的并发。
作者回复: 嗯 特意写了是MDL“读锁”。 把你的留言置顶了,希望有疑问的同学能看到这个😆
共 9 条评论266 - skyoo置顶2018-11-26FTWRL 前有读写的话 ,FTWRL 都会等待 读写执行完毕后才执行 FTWRL 执行的时候要刷脏页的数据到磁盘,因为要保持数据的一致性 ,理解的执行FTWRL时候是 所有事务 都提交完毕的时候 mysqldump + -single-transaction 也是保证事务的一致性,但他只针对 有支持事务 引擎,比如 innodb 所以 还是强烈建议大家在创建实例,表时候需要innodb 引擎 为好 全库只读 readonly = true 还有个情况在 slave 上 如果用户有超级权限的话 readonly 是失效的 表级别 锁 :一个直接就是表锁 lock table 建议不要使用, 影响太大,另个就是 MDL 元数据锁 MDL 是并发情况下维护数据的一致性,在表上有事务的时候,不可以对元数据经行写入操作,并且这个是在server层面实现的 当你做 dml 时候增加的 MDL 读锁, update table set id=Y where id=X; 并且由于隔离级别的原因 读锁之间不冲突 当你DDL 时候 增加对表的写锁, 同时操作两个alter table 操作 这个要出现等待情况。 但是 如果是 dml 与ddl 之间的交互 就更容易出现不可读写情况,这个情况容易session 爆满,session是占用内存的,也会导致内存升高 MDL 释放的情况就是 事务提交. 主库上的一个小表做了一个 DDL, 同步给slave ,由于这个时候有了先前的 single-transaction,所以slave 就会出现 该表的 锁等待, 并且slave 出现延迟展开
作者回复: 分析得很好。 尤其readonly 对 super 权限无效这句。
共 14 条评论232 - miche置顶2018-11-281. 上面的那个因为mdl锁把整个库搞挂的例子里,如果用pt工具来操作,会出现同样的情况吗? 2. 那个例子里显示select语句前加了begin,是不是select的时候不加begin,就不会出现同样的情况呢? 3. online ddl 的copy方式和inplace方式,也都是需要 拿MDL写锁、降成读锁、做DDL、升成写锁、释放MDL锁吗?展开
作者回复: 1. Pt的过程也是有操作表结构的,所以会类似 2. 对,没有begin的话,这样select执行完成以后,MDL就自动释放了哦 3. 是,是否online都是第三步(结合置顶评论看哈)的区别,另外四步还是有的
共 3 条评论38 - 马涛2019-03-02索引问题答案解释这个是不是再详细一点,我看还有人和我一样,还是搞不清楚为什么c索引和ca索引一样。
作者回复: InnoDB会把主键字段放到索引定义字段后面, 当然同时也会去重。 所以,当主键是(a,b)的时候, 定义为c的索引,实际上是(c,a,b); 定义为(c,a)的索引,实际上是(c,a,b) 你看着加是相同的 ps 定义为(c,b)的索引,实际上是(c,b,a)
共 38 条评论382 - 壹笙☞漂泊2018-11-26总结: 根据加锁范围:MySQL里面的锁可以分为:全局锁、表级锁、行级锁 一、全局锁: 对整个数据库实例加锁。 MySQL提供加全局读锁的方法:Flush tables with read lock(FTWRL) 这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句和更新类事务的提交语句等操作都会被阻塞。 使用场景:全库逻辑备份。 风险: 1.如果在主库备份,在备份期间不能更新,业务停摆 2.如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟 官方自带的逻辑备份工具mysqldump,当mysqldump使用参数--single-transaction的时候,会启动一个事务,确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。 一致性读是好,但是前提是引擎要支持这个隔离级别。 如果要全库只读,为什么不使用set global readonly=true的方式? 1.在有些系统中,readonly的值会被用来做其他逻辑,比如判断主备库。所以修改global变量的方式影响太大。 2.在异常处理机制上有差异。如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库就会一直保持readonly状态,这样会导致整个库长时间处于不可写状态,风险较高。 二、表级锁 MySQL里面表级锁有两种,一种是表锁,一种是元数据所(meta data lock,MDL) 表锁的语法是:lock tables ... read/write 可以用unlock tables主动释放锁,也可以在客户端断开的时候自动释放。lock tables语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。 对于InnoDB这种支持行锁的引擎,一般不使用lock tables命令来控制并发,毕竟锁住整个表的影响面还是太大。 MDL:不需要显式使用,在访问一个表的时候会被自动加上。 MDL的作用:保证读写的正确性。 在对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。 读锁之间不互斥。读写锁之间,写锁之间是互斥的,用来保证变更表结构操作的安全性。 MDL 会直到事务提交才会释放,在做表结构变更的时候,一定要小心不要导致锁住线上查询和更新。展开
作者回复: 早啊今天😄
共 6 条评论214 - Mr.Strive.Z.H.L2018-12-07关于文中小表DDL的疑惑: sessionC(DDL操作)被前面的sessionA和B(查询操作,获取MDL 读锁)所阻塞。这里sessionC的DDL操作任务肯定是处于等待的,后续来的sessionD(查询操作)为什么会被sessionC所阻塞? 我理解的是sessionC现在都还没有进行DDL操作,没有获取到MDL写锁,为什么sessionD会被C阻塞?难道mysql Server端对于sessionC,D有一个 队列 来决定谁先执行?展开
作者回复: “难道”正确😄
共 21 条评论171 - WL2018-12-01继续把该讲内容总结为几个问题, 大家复习的时候可以先尝试回答这些问题检查自己的掌握程度: 1. MySQL从加锁范围上分为哪三类? 2. 全局锁加锁方法的执行命令是什么?主要的应用场景是什么? 3. 做整库备份时为什么要加全局锁? 4. MySQL的自带备份工具, 使用什么参数可以确保一致性视图, 在什么场景下不适用? 5. 不建议使用set global readonly = true的方法加全局锁有哪两点原因? 6. 表级锁有哪两种类型? 各自的使用场景是什么? 7. MDL中读写锁之间的互斥关系怎样的? 8. 如何安全的给小表增加字段?展开共 1 条评论86
- Tony Du2018-11-27基于文中的例子MDL(metadata lock),自己做了一个实验(稍微有一些小改动在session D上), session A: begin; select * from t limit 1; 最先启动sessionA session B: begin; select * from t limit 1; 紧接着启动sessionB session C: alter table t add f int; 然后再是启动sessionC session D: begin; select * from t limit 1; 最后是启动sessionD 如文中例子,session A和B正常启动,然后session C被block,之后session D也被block。当把 session A 和 session B 都commit掉后,发现session C依然是block的(被 session D阻塞),只有当把 session D 也commit掉后,session C才执行下去。同样的实验,重复了三遍,结果也是一样。 从现象上看,session D会先拿到MDL读锁,当session D commit掉后,然后再是session C获得MDL写锁。请问老师,这里对于MDL锁的获取顺序(也就是说 是session C先获取MDL写锁还是session D先获取MDL读锁)有什么原则?是随机的还是有什么讲究? 另外,在一开始的获取MDL锁的阶段,session A(MDL读锁,正常执行)-> session B (MDL读锁,正常执行) -> session C (MDL写锁,被block) -> session D (MDL读锁,被MDL写锁 block) 。是不是说加MDL锁是看时间先后顺序的,一旦出现过需要获取MDL写锁(即使被block),后续再需要获取MDL读锁,则发现之前已经有获取MDL写锁(即使被block),需要获取读锁的session都会被block。感觉上像进入一个锁的”队列“,根据时间先后顺序。请问老师,这里可以更细节和深入的说明下吗? 作者回复 你这个例子里面,sessionD 被C堵住后是不能输入命令的,之后是什么动作之后,sessionD才能输入commit语句呢 我的回复: session D被C堵住后,会卡在select * from t limit 1这句。然后当我把A和B都commit掉,session D就会执行select * from t limit 1这句,此时,session C依旧会被堵着。然后把session D commit掉,session C才能执行。实验的时候,我是把sql语句都写在MySQL workbench里的,通过workbench连服务器的,session D的commit语句是写在workbench里执行的。我的问题是,为什么是session D先获取的MDL读锁,而不是session C先获取MDL写锁,对于MDL锁的获取顺序有什么原则?是随机的还是有什么讲究?展开
作者回复: 你用MySQL 客户端试试,我跑出来是文中的顺序哈。给我一下你的MySQL 版本号和workbench版本号
共 47 条评论69 - Aurora2018-11-26如果mysqldump 备份的是整个schema,某个小表t1只是该schema上其中有一张表 情况1: master上对小表t1的DDL传输到slave去应用的时刻,mysqldump已经备份完了t1表的数据,此时slave 同步正常,不会有问题。 情况2: master上对小表t1的DDL传输到slave去应用的时刻,mysqldump正在备份t1表的数据,此时会发生MDL 锁,从库上t1表的所有操作都会Hang 住。 情况3: master 上对小表t1的DDL传输到slave去应用的时刻,mysqldump 还没对t1表进行备份,该DDL会在slave的t1表应用成功,但是当导出到t1表的时候会报“ERROR 1412 (HY000): Table definition has changed, please retry transaction” 错误,导致导出失败!展开63
- 柳树2019-01-05既然session C blocked,拿不到写锁,那么session D为什么会被blocked呢?
作者回复: 如果说设计初衷,是为了防饿死吧
共 6 条评论47 - 倪大人2018-11-26思考题: 由于先用–single-transaction做备份,所以备份线程会启动一个事务获取MDL读锁,文中也说了“MDL 会直到事务提交才释放”,所以要一直等到备份完成主库来的DDL才会在从库执行生效,且备份的数据里并不会有新增的这个列。 再补充下,由于主库来的DDL会等待MDL写锁,所以会导致之后从库上的读写请求都阻塞,相当与文中sessionC和sessionD。展开42
- 栋能2018-11-26没搞懂c的索引树为什么和ca是一样的. c索引树中c有序,(a,b)随意序的呀?这能代表c与ca索引树一致吗?共 10 条评论39
- S@Y@M2018-11-28全局锁和表锁是Server层实现的吗
作者回复: 是的
共 4 条评论34 - 约书亚2018-11-27一刷的时候没注意,二刷看别人回复想起这么个问题: 例子里为什么session C之后的所有“读”请求都会被阻塞?毕竟session C还没加上锁。难道这里隐含涉及到了意向锁?
作者回复: 赞二刷态度哈 嗯,这个不是读写锁,是锁队列,一进去就开始影响后面的
共 4 条评论32 - hhhh2018-11-26表级锁的例子中: lock tables t1 read, t2 write 说到“线程A不能读取T2” 查了一下MySQL Reference: WRITE lock: The session that holds the lock can read and write the table. Only the session that holds the lock can access the table. No other session can access it until the lock is released. Lock requests for the table by other sessions block while the WRITE lock is held. 也就是说表级别write锁,对于本线程是可读可写的, 文章中说的线程A不能读取T2,我这里不太理解展开
作者回复: 是的,文中写错了。我刚刚修改上去了。抱歉。谢谢提醒
30 - 阿豆Dou2018-12-17Online DDL的过程是这样的: 1. 拿MDL写锁 2. 降级成MDL读锁 3. 真正做DDL 4. 升级成MDL写锁 5. 释放MDL锁 1、2、4、5如果没有锁冲突,执行时间非常短。第3步占用了DDL绝大部分时间,这期间这个表可以正常读写数据,是因此称为“online ” 问题 如果第四步升级为MDL写锁的时候,这个表的MDL锁有其他事务占着,那么这个事务会阻塞,等到可以拿到MDL写锁是吧展开
作者回复: 好问题, 对 而且如果不幸一直拿不到,最后锁超时了,就只好回滚这个DD了操作
共 4 条评论28 - 包包up2018-11-26没明白为什么ca索引建出来的模型和c建出来的一样?共 9 条评论28
- Tony Du2018-11-28基于文中的例子MDL(metadata lock),自己做了一个实验(稍微有一些小改动在session D上), session A: begin; select * from t limit 1; 最先启动sessionA session B: begin; select * from t limit 1; 紧接着启动sessionB session C: alter table t add f int; 然后再是启动sessionC session D: begin; select * from t limit 1; 最后是启动sessionD 如文中例子,session A和B正常启动,然后session C被block,之后session D也被block。当把 session A 和 session B 都commit掉后,发现session C依然是block的(被 session D阻塞),只有当把 session D 也commit掉后,session C才执行下去。同样的实验,重复了三遍,结果也是一样。 从现象上看,session D会先拿到MDL读锁,当session D commit掉后,然后再是session C获得MDL写锁。请问老师,这里对于MDL锁的获取顺序(也就是说 是session C先获取MDL写锁还是session D先获取MDL读锁)有什么原则?是随机的还是有什么讲究? 另外,在一开始的获取MDL锁的阶段,session A(MDL读锁,正常执行)-> session B (MDL读锁,正常执行) -> session C (MDL写锁,被block) -> session D (MDL读锁,被MDL写锁 block) 。是不是说加MDL锁是看时间先后顺序的,一旦出现过需要获取MDL写锁(即使被block),后续再需要获取MDL读锁,则发现之前已经有获取MDL写锁(即使被block),需要获取读锁的session都会被block。感觉上像进入一个锁的”队列“,根据时间先后顺序。请问老师,这里可以更细节和深入的说明下吗? 作者回复 你这个例子里面,sessionD 被C堵住后是不能输入命令的,之后是什么动作之后,sessionD才能输入commit语句呢 我的回复: session D被C堵住后,会卡在select * from t limit 1这句。然后当我把A和B都commit掉,session D就会执行select * from t limit 1这句,此时,session C依旧会被堵着。然后把session D commit掉,session C才能执行。实验的时候,我是把sql语句都写在MySQL workbench里的,通过workbench连服务器的,session D的commit语句是写在workbench里执行的。我的问题是,为什么是session D先获取的MDL读锁,而不是session C先获取MDL写锁,对于MDL锁的获取顺序有什么原则?是随机的还是有什么讲究? 作者回复 你用MySQL 客户端试试,我跑出来是文中的顺序哈。给我一下你的MySQL 版本号和workbench版本号 我的回复 用MySQL客户端试过了(通过命令行),结果和我上面实验的结果是一样的。还是session D先获取的MDL读锁,而不是session C先获取MDL写锁。 MySQL版本:Ver 14.14 Distrib 5.7.17, for Linux (x86_64) using EditLine wrapper Workbench版本: MySQL Workbench 6.3 Version 6.3.8 build 1228 CE (64 bits) Community 这里留言回复不太方便,能否留一个邮箱或者微信号,交流起来比较方便。谢谢。展开共 5 条评论17
- 李2018-12-052. 降级成MDL读锁 3. 真正做DDL 4. 升级成MDL写锁 5. 释放MDL锁 1-为什么先拿到写锁又降级成读锁, 2- 真正做DDL的时候是读锁,那这中间过程中,查询到的数据和真正的数据结构就不一致了啊 3. 为什么要先升级成写锁再释放展开
作者回复: 1. 先要写锁是要确保能改,降级是为了让后面的查询能过 2. 这个过程比较复杂(不是你想的那样😄),后面文章会说 3. 最后还得修改一下表结构
15