MySQL 全局锁是什么?原理、风险、备份踩坑完整实战

前言

做MySQL数据库备份的时候,很多同学见过 FLUSH TABLES WITH READ LOCK,这就是全局锁

全局锁是粒度最大的锁,一把锁锁住整个MySQL实例所有数据库的全部表。很多线上事故就是因为误执行全局锁,整个实例全部写入被卡住。

很多人会混淆:全局锁、表锁、MDL元数据锁。

  • 全局锁:锁住实例所有库所有表;
  • 表锁:只锁定某一张表;
  • MDL元数据锁:保护表结构元信息,表级别。

本文把MySQL全局锁讲透:作用、语法、使用场景、致命风险、备份替代方案、生产避坑。

一、什么是全局锁

全局锁会给整个MySQL实例加只读锁

加锁成功之后:

  1. 所有库的所有表,只允许读操作;
  2. DML(insert、update、delete)全部阻塞;
  3. DDL(alter、drop、truncate、rename)也全部阻塞;
  4. 普通select查询不受影响。

核心语法:

sql 复制代码
FLUSH TABLES WITH READ LOCK;

简称 FTWRL

释放锁两种方式:

sql 复制代码
-- 方式1:手动释放
UNLOCK TABLES;

-- 方式2:当前会话断开连接,锁自动释放

⚠️重点:执行 FTWRL 的会话断开,锁立刻释放。如果备份脚本中途断开,全局锁随之消失。

二、全局锁的使用场景

唯一经典场景:MyISAM引擎全库逻辑备份

MyISAM不支持事务,没有MVCC快照。如果不加全局锁,备份过程中有数据写入,导出的备份文件就会数据不一致。

所以早期MyISAM备份流程:

  1. 执行 FLUSH TABLES WITH READ LOCK; 全局只读锁
  2. mysqldump 导出全库数据
  3. UNLOCK TABLES; 释放锁

现在绝大多数业务使用InnoDB引擎,基本不再需要FTWRL全局锁

三、全局锁带来的线上致命风险

全局锁是线上高危操作,生产环境随意执行后果严重。

风险1:全实例所有写入全部阻塞

一旦执行FTWRL,实例中所有库:业务写入、订单、库存、日志表全部卡住。

大量连接堆积,线程数打满,业务接口超时雪崩。

风险2:会话意外断开,锁直接释放

脚本执行FTWRL之后,如果网络抖动,客户端会话断开,全局锁直接消失,备份一致性被破坏。

很多老备份脚本的坑。

风险3:有长查询的时候,FTWRL会被阻塞

FTWRL 执行前,需要关闭所有打开的表。

如果数据库有正在执行的慢查询、长SQL,FLUSH TABLES WITH READ LOCK 会被阻塞住,处于等待状态。

此时,后续所有DML写入请求,都会被等待中的FTWRL排队堵住,业务直接卡死。

执行顺序演示故障流程:

  1. Session A:执行一条耗时很久的SELECT慢查询,还没结束;
  2. Session B:执行 FLUSH TABLES WITH READ LOCK; → 被阻塞,等待关闭打开的表;
  3. Session C:业务执行update更新数据 → 被FTWRL等待队列阻塞;
  4. 后续所有写请求全部阻塞,业务雪崩。

注意:不是FTWRL立刻锁住,而是FTWRL在等待旧SQL结束,在等待期间就会阻塞全部写入。

四、InnoDB为什么不需要全局锁做备份

InnoDB支持事务与MVCC多版本快照。

mysqldump 参数 --single‑transaction,利用事务快照,实现热备份,不加锁

bash 复制代码
# InnoDB推荐备份命令,不加全局锁,不阻塞业务读写
mysqldump -uroot -p --single-transaction --all-databases > all.sql

原理:

  1. 开启一个可重复读RR事务;
  2. 获取一致性快照,整个备份读取这个事务快照版本;
  3. 备份期间业务可以正常增删改,不需要加任何锁。

适用条件:全部是InnoDB表。如果库里面混杂MyISAM表,--single‑transaction 对MyISAM无效,依然会出现备份数据不一致。

对比两种备份方案

备份方式 是否加全局锁 是否阻塞写入 适用引擎
mysqldump FTWRL ✅全局锁FTWRL ✅阻塞所有写入 MyISAM
mysqldump --single‑transaction ❌不加锁 ❌不阻塞业务写入 InnoDB

五、容易混淆:全局锁 vs LOCK TABLES 表锁

很多开发把两者搞混,差异很大。

  1. FLUSH TABLES WITH READ LOCK(全局锁)

    锁住实例全部数据库全部表,全局只读。

  2. 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命令,可以通过下面现象判断:

  1. show processlist,看到状态为 Waiting for table flush 的线程,大概率是FTWRL相关阻塞。
  2. 大量DML处于阻塞,无法写入,但是普通select可以正常执行。
sql 复制代码
show processlist;

出现 Waiting for table flush,说明线程在等待FTWRL释放。

处理应急:找到持有FTWRL的会话,kill掉,全局锁随会话销毁立刻释放。

八、生产开发规范

  1. InnoDB环境禁止手动执行 FLUSH TABLES WITH READ LOCK ;备份优先使用 --single‑transaction
  2. 只有遗留MyISAM库备份,才考虑FTWRL;执行前确认没有长SQL、长事务。
  3. 不要依靠 set global read_only=ON 来做数据备份一致性,super账号会绕过限制。
  4. 线上脚本不要写死FTWRL,避免脚本异常断开导致锁逻辑失效。
  5. 如果不得已要执行FTWRL,操作会话不要断开,备份完成务必执行UNLOCK TABLES。
  6. 一旦发生全局锁阻塞故障,找到对应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,全局锁直接释放。

总结

  1. MySQL全局锁由 FLUSH TABLES WITH READ LOCK(FTWRL) 实现,锁住整个实例所有库所有表,只允许读。
  2. 历史作用:MyISAM引擎逻辑备份,保障备份文件一致性。
  3. 风险极高:会阻塞全部DML/DDL写入;遇到慢查询还会反向造成业务雪崩;会话断开锁直接丢失。
  4. InnoDB生产备份优先使用 mysqldump --single‑transaction,基于MVCC快照,无锁热备。
  5. 区分:全局锁 ≠ 表锁LOCK TABLES,也不等于 read_only 全局系统变量。
  6. read_only=ON无法限制super权限账号,DDL依旧可执行,不能替代全局锁。
  7. 故障应急:kill持有FTWRL的会话,锁立刻释放。

生产记住一句话:只要你的表全部是InnoDB,业务环境尽量不要碰 FTWRL。

相关推荐
程序员夏洛6 小时前
MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?
数据库·mysql
l1258657 小时前
# RAG向量数据库优化实战:HNSW索引调参与生产级性能设计
数据库·python·mysql·langchain
梦Arrebol7 小时前
Mysql内容及相关实验
数据库·mysql
OceanWaves19937 小时前
mysql 8.0.32 磁盘爆满,清理从库日志
数据库·mysql
又见情义7 小时前
RK3568 Android 13 驱动适配实战:从零开始让SDK在自己的板子上跑起来
android·arm开发·驱动开发
clorinda8 小时前
MySQL 入门笔记:DQL 基础查询与常用函数
adb
霸道流氓气质9 小时前
MySQL 大表 DDL 变更 — 原理、方案与实践
android·数据库·mysql
JavaPub-rodert10 小时前
Go 后台如何同时兼容 MySQL、PostgreSQL、SQLite 和 SQL Server?从 ShiyuAdmin 看 GORM 多数据库适配
数据库·mysql·postgresql·golang·javapub·王仕宇
明雨-开发10 小时前
Android打包时候Execution failed for task ‘:launcher:lintVitalAnalyzeRelease‘.
android