flush tables with read lock 是迁移时唯一可靠方案,因 read_only = on 无法阻止 super 用户写操作及元数据变更,且不阻塞 create function 等非 dml 操作;ftwrl 全局锁强制所有用户(含 root)无法执行任何写入,覆盖 dml、ddl、管理命令等全部写入口。

迁移过程中必须用 FLUSH TABLES WITH READ LOCK,仅设 read_only = ON 不够安全。
为什么 read_only = ON 在迁移时不可靠
它只拦截普通用户的写操作,SUPER 权限账号(比如 root)仍可执行 INSERT、UPDATE、DROP TABLE 等任意变更语句;迁移期间若有人用 root 登录误操作,数据一致性就破了。更关键的是:read_only 不阻塞 CREATE FUNCTION、CREATE EVENT、ANALYZE TABLE 这类“非 DML 但会改元数据或统计信息”的操作——这些在迁移窗口里也该禁止。
常见错误现象:
- 迁移前已执行
SET GLOBAL read_only = ON,但运维仍能用 root 成功执行UPDATE users SET name='x' WHERE id=1 - 备份脚本运行中,另一个会话用 root 执行
OPTIMIZE TABLE orders,导致表结构临时变化、备份不一致
FLUSH TABLES WITH READ LOCK 是迁移阶段的真正兜底手段
这条命令加的是全局读锁,所有用户(含 SUPER)都无法执行任何写操作,包括 DML、DDL、事务提交、甚至 TRUNCATE 和 DROP DATABASE。它和 read_only 是正交机制:前者作用于存储层,后者作用于 SQL 层权限判断。
使用场景与注意事项:
- 必须在迁移开始前立即执行,且保持连接不中断(断开后锁自动释放)
- 执行后立刻检查
SHOW PROCESSLIST,确认没有长事务或正在运行的写入线程卡住锁 - 不能和
--single-transaction同时用——后者依赖 MVCC,而 FTWRL 会强制所有事务走一致性读快照,反而可能引发死锁 - MyISAM 表必须用 FTWRL,InnoDB 表虽可用
--single-transaction,但迁移要求“绝对零写入”时,FTWRL 更稳妥
迁移完成后如何安全恢复写入
别只执行 UNLOCK TABLES 就完事。要按顺序做三件事:
- 先确认主从复制没积压:
SHOW SLAVE STATUS\G中Seconds_Behind_Master: 0且Slave_SQL_Running: Yes - 再执行
UNLOCK TABLES—— 注意,这个命令只能由持有锁的会话执行,不能换连接 - 最后才关闭
read_only(如果之前设过):SET GLOBAL read_only = OFF,否则应用连上来发现写不了会报ERROR 1290
容易踩的坑:UNLOCK TABLES 后忘记关 read_only,或者反过来,先关 read_only 再解锁,中间空窗期可能被写入。
要不要启用 super_read_only?
它适合长期只读场景(如从库),但迁移是临时操作,没必要动它。super_read_only = ON 虽能拦住 root 的 DML,却拦不住 FLUSH LOGS、PURGE BINARY LOGS、INSTALL PLUGIN 这些管理命令,而 FTWRL 连这些都锁死。另外,5.7.8 以下版本不支持 super_read_only,兼容性差。
一句话总结:迁移窗口里,FLUSH TABLES WITH READ LOCK 是唯一能覆盖所有写入口的方案,read_only 只能当辅助验证用——比如解锁后快速跑个 SELECT @@global.read_only 确认没被意外打开。











