不,只靠GRANT SELECT不够;必须先SHOW GRANTS检查残留写权限,再REVOKE INSERT/UPDATE/DELETE/DROP/SUPER等所有写类权限,最后用新连接验证SELECT成功且INSERT失败。

只靠 GRANT SELECT 就够了吗?不,必须确认没残留写权限
很多人执行完 GRANT SELECT ON `mydb`.* TO 'ro_user'@'%' 就以为完成了,结果发现用户还能 DROP TABLE 或 INSERT。这是因为 MySQL 权限是叠加的:哪怕之前只执行过一次 GRANT ALL PRIVILEGES,后续只加 SELECT 并不会自动清除旧权限。必须主动查、主动撤。
先运行:SHOW GRANTS FOR 'ro_user'@'%';
重点看输出里是否含 INSERT、UPDATE、DROP、ALTER、LOCK TABLES、EXECUTE、GRANT OPTION,甚至 SUPER —— 任何一项存在,都意味着“只读”不成立。
常见残留来源:
- 历史用
GRANT ALL ON *.*创建过该用户 - 用户属于某个角色(MySQL 8.0+),而角色自带写权限
- 用
UPDATE mysql.user直接改表,导致权限字段未同步刷新
REVOKE 写权限时,哪些操作必须显式执行
不能只依赖“没给就不让做”,必须显式撤销已存在的写类权限。对目标库 `reporting_db`,以下语句缺一不可:
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES, EXECUTE, TRUNCATE, CREATE TEMPORARY TABLES ON `reporting_db`.* FROM 'ro_user'@'%';
特别注意:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
LOCK TABLES必须撤——否则SELECT ... FOR UPDATE可能意外成功(虽然业务上不该用,但权限层面不能留洞) -
TRUNCATE是独立权限,不是DROP的子集,漏掉就等于留了清空表的后门 - 如果
SELECT User, Host, Super_priv FROM mysql.user WHERE User = 'ro_user'返回Y,必须补一句:REVOKE SUPER ON *.* FROM 'ro_user'@'%';
验证时最容易忽略的三个连接细节
权限改完不生效?大概率是验证方式错了。MySQL 权限在连接建立时快照加载,旧连接不会自动更新。
务必做到:
- 用全新客户端重新登录:
mysql -u ro_user -p -h your-db-host,别复用当前 shell 里的会话 - 登录后立刻执行:
SELECT CURRENT_USER();确认匹配的是你授权的账号(比如'ro_user'@'10.20.%',而不是'ro_user'@'localhost') - 再跑两条关键测试:
→SELECT COUNT(*) FROM reporting_db.users;(应成功)
→INSERT INTO reporting_db.users(id) VALUES(1);(必须报ERROR 1142 (42000): INSERT command denied)
如果 INSERT 没报错,说明 REVOKE 漏项或对象不匹配;如果 SELECT 都失败,先检查库名是否用了反引号(如 `order_db`)、host 是否精确匹配。
MySQL 8.0+ 下 FLUSH PRIVILEGES 还需要吗
在 MySQL 5.7 及更早版本中,FLUSH PRIVILEGES 是必须的;但从 8.0 开始,CREATE USER 和 GRANT/REVOKE 本身就会立即持久化并通知所有连接线程——FLUSH PRIVILEGES 不仅多余,还可能掩盖问题(比如误以为执行它就“生效了”,实则 REVOKE 根本没写对)。
所以建议:
- 8.0+ 环境下,只在修改
mysql.user表后才用FLUSH PRIVILEGES(正常操作不应直接改系统表) - 执行完
GRANT和REVOKE后,直接新开连接验证,不要加FLUSH - 若验证失败,优先排查
SHOW GRANTS FOR CURRENT_USER;输出,而不是反复刷权限
真正容易被绕过的点不在命令顺序,而在 host 匹配粒度、角色继承、以及 information_schema 这类元数据库的隐式访问——这些不显眼,但一旦出问题,连 SHOW CREATE TABLE 都会失败,让人误判权限配置本身有 bug。

















