MySQL经验一文为你解答全局锁和表锁到

北京青春痘医院好 http://m.39.net/pf/a_8736666.html

从这一节开始,我们进入专栏的第三个MySQL知识大类:MySQL锁。

在上一章(MySQL索引)中,我们介绍了索引原理、需要添加索引的场景、一些常见索引类型的区别等,以及分享了有些场景MySQL会选错索引及选错索引时的处理方式等。通过这些学习,我们知道了提高查询效率的方法。但是,数据库往往是多个用户或者客户端在连接使用的。这时,我们需要考虑一个新的问题:

如何保证数据并发访问的一致性、有效性呢?

MySQL中,锁就是协调多个用户或者客户端并发访问某一资源的机制,保证数据并发访问时的一致性和有效性。

本章就来介绍一下不同场景下的锁机制。

根据加锁的范围,MySQL中的锁可分为三类:

全局锁表级锁行锁本节来重点讲解一下全局锁和表锁。

1全局锁

MySQL全局锁会关闭所有打开的表,并使用全局读锁锁定所有表。其命令为:FLUSHTABLESWITHREADLOCK;

简称:FTWRL,可以使用下面命令解锁:UNLOCKTABLES;

我们来通过实验理解一下全局锁:

首先创建测试表,并写入数据:

usetest;

droptableifexistst14;

CREATETABLE`t14`(

`id`int(11)NOTNULLAUTO_INCREMENT,

`a`int(11)NOTNULL,

`b`int(11)NOTNULL,

PRIMARYKEY(`id`),

KEY`idx_a`(`a`)

)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

insertintot14(a,b)values(1,1);

进行FTWRL实验:

上面的实验中,当session1执行FTWRL后,本线程session1和其它线程session2都可以查询,本线程和其它线程都不能更新。

原因是:当执行FTWRL后,所有的表都变成只读状态,数据更新或者字段更新将会被阻塞。

那么全局锁一般什么时候会用到呢?

全局锁一般用在整个库(包含非事务引擎表)做备份(mysqldump或者xtrabackup)时。也就是说,在整个备份过程中,整个库都是只读的,其实这样风险挺大的。如果是在主库备份,会导致业务不能修改数据;而如果是在从库备份,就会导致主从延迟。

好在mysqldump包含一个参数--single-transaction,可以在一个事务中创建一致性快照,然后进行所有表的备份。因此增加这个参数的情况下,备份期间可以进行数据修改。但是需要所有表都是事务引擎表。所以这也是建议使用InnoDB存储引擎的原因之一。

而对于xtrabackup,可以分开备份InnoDB和MyISAM,或者不执行--master-data,可以避免使用全局锁。

2表级锁

表级锁有两种:表锁和元数据锁。

2.1表锁

表锁使用场景:事务需要更新某张大表的大部分或全部数据。如果使用默认的行锁,不仅事务执行效率低,而且可能造成其它事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高事务执行速度;事务涉及多个表,比较复杂,可能会引起死锁,导致大量事务回滚,可以考虑表锁避免死锁。其中表锁又分为表读锁和表写锁,命令分别是:

表读锁:locktablest14read;

表写锁:locktablest14write;

下面我们分别用实验验证表读锁和表写锁。

表读锁实验:

从上面的实验我们可以看出,在session1中对表t14加表读锁,session1和session2都可以查询表t14的数据;而session1执行更新会报错,session2执行更新会等待(直到session1解锁后才更新成功)。

总结:对表执行locktablesxxxread(表读锁)时,本线程和其它线程可以读,本线程写会报错,其它线程写会等待。

我们再来看一下表写锁实验:

总结:对表执行locktablesxxxwrite(表写锁)时,本线程可以读写,其它线程读写都会阻塞。

2.2元数据锁

在MySQL中,DDL是不属于事务范畴的。如果事务和DDL并行执行同一张表时,可能会出现事务特性被破坏、binlog顺序错乱等bug。为了解决这类问题,从MySQL5.5.3开始,引入了元数据锁(MetadataLocking,简称:MDL锁)。

从上面我们知道,MDL锁的出现解决了同一张表上事务和DDL并行执行时可能导致数据不一致的问题。

但是,我们在工作中,很多情况需要考虑MDL的存在,否则可能导致长时间锁等待甚至连接被打满的情况。如下例:

上面的实验中,我们在session1查询了表t14的数据,其中使用了sleep(),表示在秒后才会返回结果;然后在session2执行DDL操作时会等待(原因是session1执行期间会对表t14加一个MDL,而session2又会跟session1争抢MDL);而session3执行查询时也会继续等待。因此如果session1的语句一直没结束,其它所有的查询都会等待。

这种情况下,如果这张表查询比较频繁,很可能短时间把数据库的连接数打满,导致新的连接无法建立而报错,如果是正式业务,影响是非常恐怖的。

当然如果出现这种情况,假如你还有session连着数据库,可以kill掉session1中的语句或者终止session2中的DDL操作,可以让业务恢复。但是出现这种情况的根源其实是:session1中有长时间未提交的事务。

因此对于开发来说,在工作中应该尽量避免慢查询、尽量保证事务及时提交、避免大事务等,当然对于DBA来说,也应该尽量避免在业务高峰执行DDL操作。




转载请注明:http://www.aierlanlan.com/grrz/2682.html

  • 上一篇文章:
  •   
  • 下一篇文章: