冷备库账号仍能执行insert或drop,因其权限是叠加而非覆盖的:曾授all privileges或super等写权限后,仅加grant select无效;必须显式revoke所有写类权限及super,并验证无角色继承。

只授 SELECT 不够,必须显式撤掉所有写权限,并确认没残留 SUPER 或全局权限。
为什么冷备库账号仍能执行 INSERT 或 DROP?
冷备归档库(如 archive_2024_q3)本质仍是普通数据库,MySQL 不会自动将其设为只读。用户只要曾被授予过 INSERT、DROP 或 ALL PRIVILEGES ON *.*,哪怕只执行过一次,后续只加 GRANT SELECT 也无效——权限是叠加的,不是覆盖的。
- 执行
SHOW GRANTS FOR 'archiver_ro'@'%';,检查输出里是否含INSERT、UPDATE、DROP、ALTER、LOCK TABLES、TRUNCATE、CREATE TEMPORARY TABLES或SUPER - 特别注意
USAGE权限:它不等于“只读”,只是连接基础;而ALL PRIVILEGES ON *.*一旦存在,必须先REVOKE ALL PRIVILEGES ON *.* FROM 'archiver_ro'@'%' - MySQL 8.0+ 中,若用户绑定了角色(role),还要查
SELECT * FROM mysql.role_edges WHERE TO_HOST = 'archiver_ro';,避免角色继承写权限
正确创建冷备只读账号的四步操作
以账号 archiver_ro 访问库 archive_2024_q3 为例,生产环境推荐流程:
- 创建用户并限制来源:
CREATE USER 'archiver_ro'@'10.100.50.%' IDENTIFIED WITH mysql_native_password BY 'A1#cold2026';(不用%,防横向渗透) - 只授目标库读权限:
GRANT SELECT ON `archive_2024_q3`.* TO 'archiver_ro'@'10.100.50.%';(反引号防库名含短横线出错) - 显式撤所有写类权限:
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES, EXECUTE, TRUNCATE, CREATE TEMPORARY TABLES ON `archive_2024_q3`.* FROM 'archiver_ro'@'10.100.50.%'; - 确认并撤销全局高危权限:
SELECT Super_priv FROM mysql.user WHERE User = 'archiver_ro';,若为Y,立刻执行REVOKE SUPER ON *.* FROM 'archiver_ro'@'10.100.50.%';
最后执行 FLUSH PRIVILEGES; —— 虽然正常 GRANT 后可不刷,但跨版本迁移或手动改过 mysql.db 表后这步不能跳。
验证时最容易忽略的三个点
用新连接(不是复用 root 会话)登录后测试:
-
SELECT COUNT(*) FROM archive_2024_q3.orders;→ 必须成功 -
INSERT INTO archive_2024_q3.orders(id) VALUES(1);→ 必须报ERROR 1142 (42000): INSERT command denied -
SELECT * FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'archive_2024_q3';→ 若需支持此查询,得额外加GRANT SELECT ON `information_schema`.`TABLES` TO 'archiver_ro'@'10.100.50.%';,否则会报权限错
别信 SHOW DATABASES 输出——它列出所有库名不代表能进,USE mysql; 一定会失败;真正要盯的是 USE archive_2024_q3; 能否成功 + 后续 DML 是否真被拦住。
冷备库常被误认为“天然只读”,其实 MySQL 对它的访问控制逻辑和业务库完全一致。最危险的不是忘了 GRANT,而是没清理干净历史 GRANT ALL ON *.* 或角色继承的权限。











