思考
全局锁
1.全局锁 Flush tables with read lock (FTWRL) Unlock tables解锁
2.全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都 select 出来存成文本。比如 MyISAM 这种不支持一致性读的存储引擎做全库备份时需要使用全局锁,像 InnoDB 引擎做全库备份时不需要使用全局锁。
3.FTWRL确保其他线程DML DDL不会对表有影响
4.加锁的好处:保证视图逻辑一致与mysqldump事务--single-transaction(导数据之前启动一个事务来保证一致性视图)
使用 --single-transaction弊端:只支持事务引擎,mysql其他引擎无法使用
5.全局锁与set global readonly=true对比
- 有些系统 set global readonly=true 这个命令经常用在判断一个库是主库还是从库
- 当系统发生异常的时候,FTWRL会自动释放这个全局锁,但是只读状态会一直保持这个状态,导致数据库不可写,风险系数高。
表锁
1.表级锁分为两种:表锁和元数据锁(MDL)
举个例子, 如果在某个线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不允许,自然也不能访问其他表。
2.MDL锁不需要显示使用,访问一个表会自动加上
MDL 的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。
3.DML加读锁 DDL加写锁,读锁之间不互斥,多个线程可以对同一张表进行增删改查,读写锁之间,写锁之间互斥
实践
全局锁
flush tables with read lock
[SQL]flush tables with read lock 受影响的行: 0 时间: 0.010s
此时对数据库执行DML操作,数据库一直处在阻塞状态。
insert into d(`id`) VALUES(1)
[SQL]insert into d(`id`) VALUES(1)
此时执行解锁操作
unlock tables
[SQL]unlock TABLES 受影响的行: 0 时间: 0.000s
再执行插入操作,数据库已经可以插入数据了
insert into d(`id`) VALUES(1)
表锁
开启一个事务执行查询语句
begin;
SELECT * from article
结果:查询到数据
继续查询这张表
SELECT * from article
结果:查询到数据
增加字段操作
alter table `article` add COLUMN b INT;
结果:阻塞
继续查询操作
select * from `article` ;
结果:阻塞
alter table `article` add COLUMN b INT;
生成MDL写锁,第一次查询的读锁还没有释放,读写锁之间互斥造成阻塞
问题
不过令一个实验让我有点费解
navicat新建查询进行锁表,再次新建查询进行解锁失败。
答:??
如何避免这种阻塞情况?
答:1.kill掉这个长事务
2.设置超时时间如果在等待时间能拿到写锁最好,拿不到就等一会让DBA再次执行语句
MariaDB 已经合并了 AliSQL 的这个功能,所以这两个开源分支目前都支持 DDL NOWAIT/WAIT n 这个语法。
ALTER TABLE tbl_name NOWAIT add column ...
ALTER TABLE tbl_name WAIT N add column ...
结论
1.全局锁主要用在逻辑备份过程,对于全部InnoDB引擎的库最好使用single-transaction 参数
2.表锁一般是在数据库引擎不支持行锁的时候才会被用到的。如果你发现你的应用程序里有 lock tables 这样的语句,你需要追查一下,比较可能的情况是:要么是你的系统现在还在用 MyISAM 这类不支持事务的引擎,那要安排升级换引擎;要么是你的引擎升级了,但是代码还没升级。我见过这样的情况,最后业务开发就是把 lock tables 和 unlock tables 改成 begin 和 commit,问题就解决了。
3.MDL 会直到事务提交才释放,在做表结构变更的时候,你一定要小心不要导致锁住线上查询和更新。
摘要内容来源:极客时间Mysql45讲--全局锁和表锁:给字段怎么有这么多障碍?