mysql - (全局锁、表锁)

mysql 锁

根据加锁的范围,MySQL 里面的锁大致可以分成全局锁、表级锁、行锁
  1. 全局锁
  • 全局锁就是对整个数据库实例加锁,mysql提供了一种加全局锁的方法,命令是Flush tables with read lock (FTWRL),当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其它线程的以下语句会被阻塞:数据更新语句(DML-增删改)、数据定义语句(DDL:包括建表、修改表结构等)和更新类事物的提交语句。
  • 全局锁的典型使用场景:做全库逻辑备份(也就是把整库每个表都select出来存成文本)
  • 全局的弊端:业务停摆,主从延迟等;
  • 使用官方自带的逻辑备份工具mysqldump,当mysqldump使用参数-single-transaction的时候,导数据之前就会启动一个事物,来确保拿到视图的一致性(其实就是事物隔离机制的可重复读),这个需要数据库引擎支持这个隔离级别,如果数据库引擎不支持事物或者不支持这个隔离级别,就只能使用FTWRL这种方式了。
  1. 表级锁
  • mysql的表级锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。
  • 表锁的语法 lock tables … read/write。可以用unlock tables 主动释放锁,也可以在客户端断开的时候主动释放。
  • 另一类元数据锁是MDL(metadata lock),这个不需要显式使用,在访问一个表的时候会被自动加上,MDL的作用是,保证读写的正确性。
    1. 特性:读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
    2. 读写锁之间、写锁之间互斥,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才行;
  • MDL锁的一个坑:
    mysql - (全局锁、表锁)
    sessionA先启动,这是加一个MDL读锁,由于sessionB需要的也是MDL读锁,所以正常执行;
    之后sessinC 会被blocked,因为sessinA 的MDL读锁还没有释放,而sessionC 需要MDL写锁,因此被阻塞。
    只有sessionC 被阻塞还没关系,如果之后所有的表t上新申请的MDL读锁的请求也会被sessionC阻塞,如果某个表上的查询语句频繁,而且客户端有重试机制,也就是说超时后悔再起一个新的session再请求的话,这个库的线程就会爆满。

———————————————————
解决办法,解决长事物,事物不提交,就会一直占用MDL锁,在mysql的information_schema库的innodb_trx表中,你可以查到当前执行的事物,如果要做DDL变更,要考虑先暂停DDL,或者kill掉长事物。
如果这个是热点表,上面的请求很频繁,这时候kill可能未必管用,因为新的请求马上就来了,比较理想的机制是,在alter table 语句里面设定等待时间,如果在这个指定时间里能够拿到MDL写锁最好,拿不到也不要阻塞后面的业务语句,先放弃,之后开发人员或者DBA再通过重试命令重复这个过程;