MySQL只读权限需先显式回收残留写权限(如INSERT、UPDATE等),再授予SELECT,否则权限叠加导致仍可修改;必须限定登录主机、逐库授权、补充information_schema最小查询权限,并刷新后重连验证。

只读权限不是“加个 SELECT 就完事”,开发人员若已有旧账号、残留全局权限或匹配了更宽泛的 User@Host 规则,GRANT SELECT 本身不会覆盖或清除写权限——必须先 REVOKE,再 GRANT,否则照样能 INSERT 或 DROP。
为什么开发人员执行 SELECT 成功但还能 UPDATE?
常见原因是权限未清理干净。MySQL 权限是叠加生效的,且按 User@Host 精确匹配优先级排序。比如用户 'dev'@'%' 之前被授予过 GRANT ALL ON *.*,后来你只跑了一句:
GRANT SELECT ON `appdb`.* TO 'dev'@'%';
这不会删掉他原有的 UPDATE 权限,只是多加了一条 SELECT。结果就是:查得动,改得也动。
- 先检查真实权限:
SHOW GRANTS FOR 'dev'@'%'; - 若输出里有
INSERT、UPDATE、ALTER等,说明残留权限还在 - 必须显式回收:
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES ON *.* FROM 'dev'@'%'; - 再授只读:
GRANT SELECT ON `appdb`.* TO 'dev'@'%'; - 最后别跳过:
FLUSH PRIVILEGES;
开发用账号要不要限制主机?
要,而且强烈建议。开发环境常混用测试库和生产库连接配置,如果账号允许 'dev'@'%',一旦本地 IDE 配置错 host,可能直连生产实例。更安全的做法是限定子网或跳板机 IP:
CREATE USER 'dev'@'192.168.50.%' IDENTIFIED BY 'StrongPass2026!';GRANT SELECT ON `appdb`.* TO 'dev'@'192.168.50.%';- 避免用
'dev'@'localhost'—— Docker 容器内 MySQL 客户端常走 TCP,localhost不匹配,实际连不上
SHOW CREATE TABLE 和 DESCRIBE 报错怎么办?
开发人员常用这些命令看表结构,但它们底层依赖对 information_schema 的查询权限。MySQL 8.0+ 默认不自动开放这部分元数据访问,即使你给了 SELECT ON appdb.*,仍可能报:
ERROR 1142 (42000): SHOW command denied to user
解决方法是补一条最小授权:
GRANT SELECT ON `information_schema`.`TABLES` TO 'dev'@'192.168.50.%';- 如需
SHOW CREATE VIEW或列注释,再加:GRANT SELECT ON `information_schema`.`COLUMNS` TO 'dev'@'192.168.50.%'; - 不要授整个
information_schema.*—— 里面含敏感统计信息(如PROCESSLIST)
跨多个业务库只读怎么授权?
不能偷懒写 GRANT SELECT ON *.*,那等于把 mysql、sys、performance_schema 全部暴露给开发,存在信息泄露风险。正确做法是逐库明确列出:
GRANT SELECT ON `appdb`.* TO 'dev'@'192.168.50.%';GRANT SELECT ON `logdb`.* TO 'dev'@'192.168.50.%';GRANT SELECT ON `reportdb`.* TO 'dev'@'192.168.50.%';- 每加一个库,都得同步
REVOKE对应库的写权限(比如之前有GRANT ALL ON logdb.*,就得单独REVOKE)
最容易被忽略的是:权限修改后,开发人员当前连接会话仍保留旧权限,必须断开重连才能验证真实效果。别在同一个终端里 GRANT 后立刻 INSERT 测试——那测的是缓存,不是新策略。


















