全局锁的典型使用场景是,做全库逻辑备份
历史做法:flush table with read lock;
确保不会有其他线程对数据库做更新,然后对整个库做备份。
mysql> flush table with read lock; Query OK, 0 rows affected (0.10 sec) mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 5 | 5 | 5 | | 10 | 10 | 55 | | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | +----+------+------+ 6 rows in set (0.01 sec) mysql> update d set d = 99 where id = 0; ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
缺陷:
官方自带的逻辑备份工具是 mysqldump,mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。
能够支持事务隔离的,就可以通过事务隔离的手段来做到,有的引擎不支持(如:MyISAM不支持),需要通过FTWRL来给全局上锁
语法:lock tables … read/write
解除:unlock tables
写锁(排他锁):自己能写
读锁(共享锁):都能读,不能写
对于 InnoDB 这种支持行锁的引擎,一般不使用 lock tables 命令来控制并发,毕竟锁住整个表的影响面还是太大。
MDL(MySQL 5.5 版本中引入) 的作用是,保证读写的正确性,MDL 不需要显式使用,在访问一个表的时候会被自动加上。
读锁与读锁止键不互斥,写锁与写锁,读锁与写锁之间互斥,用来保证变更表结构操作的安全性
注意安全性:
session A 先启动,这时候会对表 t 加一个 MDL 读锁。
session B 需要的也是 MDL 读锁,因此可以正常执行。
session C 会被 blocked,是因为 session A 的 MDL 读锁还没有释放。session D 也会被 blocked。
导致问题:如果查询语句频繁,且客户端有重试机制会打爆Mysql
结论:事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放
思想:防止死锁,加入超时时间机制
MariaDB 已经合并了 AliSQL 的这个功能,所以这两个开源分支目前都支持 DDL NOWAIT/WAIT n 这个语法
ALTER TABLE tbl_name NOWAIT add column ... ALTER TABLE tbl_name WAIT N add column ...
MySQL 8.0.1在select语法中给出了NOWAIT特性(官方博客),也就是我们可以在查询时如果需要等待行锁,则立即退出报错。这种特性其实也可以用在MDL锁,这样我们如果在进行DDL语句时,通过NOWAIT检测一下是否有相关元数据锁等待,如果有则立即退出,这样运维人员即可及时判断出能否进行ddl操作,而不至于在执行ddl时,造成大量业务中断。