前言
做MySQL数据库备份的时候,很多同学见过 FLUSH TABLES WITH READ LOCK,这就是全局锁 。
全局锁是粒度最大的锁,一把锁锁住整个MySQL实例所有数据库的全部表。很多线上事故就是因为误执行全局锁,整个实例全部写入被卡住。
很多人会混淆:全局锁、表锁、MDL元数据锁。
- 全局锁:锁住实例所有库所有表;
- 表锁:只锁定某一张表;
- MDL元数据锁:保护表结构元信息,表级别。
本文把MySQL全局锁讲透:作用、语法、使用场景、致命风险、备份替代方案、生产避坑。
一、什么是全局锁
全局锁会给整个MySQL实例加只读锁 。
加锁成功之后:
- 所有库的所有表,只允许读操作;
- DML(insert、update、delete)全部阻塞;
- DDL(alter、drop、truncate、rename)也全部阻塞;
- 普通select查询不受影响。
核心语法:
sql
FLUSH TABLES WITH READ LOCK;
简称 FTWRL。
释放锁两种方式:
sql
-- 方式1:手动释放
UNLOCK TABLES;
-- 方式2:当前会话断开连接,锁自动释放
⚠️重点:执行 FTWRL 的会话断开,锁立刻释放。如果备份脚本中途断开,全局锁随之消失。
二、全局锁的使用场景
唯一经典场景:MyISAM引擎全库逻辑备份
MyISAM不支持事务,没有MVCC快照。如果不加全局锁,备份过程中有数据写入,导出的备份文件就会数据不一致。
所以早期MyISAM备份流程:
- 执行
FLUSH TABLES WITH READ LOCK;全局只读锁 - mysqldump 导出全库数据
UNLOCK TABLES;释放锁
现在绝大多数业务使用InnoDB引擎,基本不再需要FTWRL全局锁。
三、全局锁带来的线上致命风险
全局锁是线上高危操作,生产环境随意执行后果严重。
风险1:全实例所有写入全部阻塞
一旦执行FTWRL,实例中所有库:业务写入、订单、库存、日志表全部卡住。
大量连接堆积,线程数打满,业务接口超时雪崩。
风险2:会话意外断开,锁直接释放
脚本执行FTWRL之后,如果网络抖动,客户端会话断开,全局锁直接消失,备份一致性被破坏。
很多老备份脚本的坑。
风险3:有长查询的时候,FTWRL会被阻塞
FTWRL 执行前,需要关闭所有打开的表。
如果数据库有正在执行的慢查询、长SQL,
FLUSH TABLES WITH READ LOCK会被阻塞住,处于等待状态。此时,后续所有DML写入请求,都会被等待中的FTWRL排队堵住,业务直接卡死。
执行顺序演示故障流程:
- Session A:执行一条耗时很久的SELECT慢查询,还没结束;
- Session B:执行
FLUSH TABLES WITH READ LOCK;→ 被阻塞,等待关闭打开的表; - Session C:业务执行update更新数据 → 被FTWRL等待队列阻塞;
- 后续所有写请求全部阻塞,业务雪崩。
注意:不是FTWRL立刻锁住,而是FTWRL在等待旧SQL结束,在等待期间就会阻塞全部写入。
四、InnoDB为什么不需要全局锁做备份
InnoDB支持事务与MVCC多版本快照。
mysqldump 参数 --single‑transaction,利用事务快照,实现热备份,不加锁。
bash
# InnoDB推荐备份命令,不加全局锁,不阻塞业务读写
mysqldump -uroot -p --single-transaction --all-databases > all.sql
原理:
- 开启一个可重复读RR事务;
- 获取一致性快照,整个备份读取这个事务快照版本;
- 备份期间业务可以正常增删改,不需要加任何锁。
适用条件:全部是InnoDB表。如果库里面混杂MyISAM表,
--single‑transaction对MyISAM无效,依然会出现备份数据不一致。
对比两种备份方案
| 备份方式 | 是否加全局锁 | 是否阻塞写入 | 适用引擎 |
|---|---|---|---|
| mysqldump FTWRL | ✅全局锁FTWRL | ✅阻塞所有写入 | MyISAM |
| mysqldump --single‑transaction | ❌不加锁 | ❌不阻塞业务写入 | InnoDB |
五、容易混淆:全局锁 vs LOCK TABLES 表锁
很多开发把两者搞混,差异很大。
-
FLUSH TABLES WITH READ LOCK(全局锁)
锁住实例全部数据库全部表,全局只读。
-
LOCK TABLES t1 READ;(表锁)
仅仅锁定某一张指定表,其他库、其他表不受任何影响。
sql
-- 只锁住user这一张表,别的表可以正常写入
LOCK TABLES `user` READ;
六、全局锁和set global read_only=ON的区别
另一种设置全局只读的方式:
sql
SET GLOBAL read_only = ON;
很多人以为和FTWRL等价,二者不一样。
| FLUSH TABLES WITH READ LOCK | set global read_only=ON | |
|---|---|---|
| 作用 | 全局锁,锁表 | 系统变量,控制普通用户只读 |
| super权限账号 | super账号依然可以写 | super管理员账号仍然可以写入数据 |
| DDL操作 | 全部DDL被阻塞 | DDL也可以正常执行 |
| 会话断开 | 会话断开锁自动释放 | 全局参数永久生效,手动改回OFF才解除 |
关键点:
read_only=ON 只能限制普通业务账号,拥有SUPER权限root账号不受限制;DDL语句依旧能够执行。
真正做一致性备份不能只靠 read_only 参数。
七、如何查看是否存在全局锁
MySQL没有直接show global locks命令,可以通过下面现象判断:
- show processlist,看到状态为
Waiting for table flush的线程,大概率是FTWRL相关阻塞。 - 大量DML处于阻塞,无法写入,但是普通select可以正常执行。
sql
show processlist;
出现 Waiting for table flush,说明线程在等待FTWRL释放。
处理应急:找到持有FTWRL的会话,kill掉,全局锁随会话销毁立刻释放。
八、生产开发规范
- InnoDB环境禁止手动执行 FLUSH TABLES WITH READ LOCK ;备份优先使用
--single‑transaction。 - 只有遗留MyISAM库备份,才考虑FTWRL;执行前确认没有长SQL、长事务。
- 不要依靠
set global read_only=ON来做数据备份一致性,super账号会绕过限制。 - 线上脚本不要写死FTWRL,避免脚本异常断开导致锁逻辑失效。
- 如果不得已要执行FTWRL,操作会话不要断开,备份完成务必执行UNLOCK TABLES。
- 一旦发生全局锁阻塞故障,找到对应session kill,锁立即释放。
九、常见疑问
Q1:FTWRL 会阻塞普通select查询吗?
A:不会。普通SELECT快照读可以正常执行;DML、DDL被阻塞。
Q2:--single‑transaction 能不能备份混合MyISAM+InnoDB库?
A:不行。MyISAM不支持事务快照,MyISAM表备份时刻仍然会发生数据变更,导出文件不一致。MyISAM表还是需要FTWRL。
Q3:kill会话之后,FTWRL全局锁会释放吗?
A:会,执行FTWRL的会话被kill,全局锁直接释放。
总结
- MySQL全局锁由
FLUSH TABLES WITH READ LOCK(FTWRL)实现,锁住整个实例所有库所有表,只允许读。 - 历史作用:MyISAM引擎逻辑备份,保障备份文件一致性。
- 风险极高:会阻塞全部DML/DDL写入;遇到慢查询还会反向造成业务雪崩;会话断开锁直接丢失。
- InnoDB生产备份优先使用
mysqldump --single‑transaction,基于MVCC快照,无锁热备。 - 区分:全局锁 ≠ 表锁LOCK TABLES,也不等于 read_only 全局系统变量。
- read_only=ON无法限制super权限账号,DDL依旧可执行,不能替代全局锁。
- 故障应急:kill持有FTWRL的会话,锁立刻释放。
生产记住一句话:只要你的表全部是InnoDB,业务环境尽量不要碰 FTWRL。