正确创建MySQL只读账号需分三步:先CREATE USER并限定IP,再GRANT USAGE确保登录,最后GRANT SELECT到指定库;还需额外授权information_schema.TABLES和COLUMNS以支持SHOW CREATE TABLE,并推荐用角色管理权限且显式设置DEFAULT ROLE。

只读账号必须显式授予 SELECT,不能靠“没给写权限”来假设安全
MySQL 权限是白名单机制:不授就是不能做,但授了什么才决定能做什么。只执行 REVOKE INSERT, UPDATE 等操作不仅多余,还极易遗漏——比如漏掉 TRUNCATE、LOCK TABLES 或 EXECUTE,这些都可能被用于绕过只读限制。正确做法是从零开始,只授 SELECT,其他一概不碰。
常见错误包括:
- 写成
GRANT SELECT ON *.*—— 这会把performance_schema、sys等系统库也放开,暴露结构信息 - 用单引号包库名,如
'my-app'.* —— 语法错误,应写成`my-app`.*或无特殊字符时直接写myapp.* - 跨库 JOIN 查询失败却归因为网络或连接问题 —— 实际是没对第二个库单独执行
GRANT SELECT ON db2.*
CREATE USER 和 GRANT 必须分两步,且新用户需补 USAGE 才能登录
MySQL 8.0 不支持在 CREATE USER 语句中直接带 GRANT,强行合写会报错:ERROR 1064 (42000): You have an error in your SQL syntax。更关键的是,GRANT SELECT 只管“能查什么”,不管“能不能连上”——漏掉 USAGE 权限会导致认证通过后立即断连,报错 ERROR 1045 (28000): Access denied for user,哪怕密码完全正确。
标准流程是:
- 先创建用户(建议限定 IP 段,避免用
'%'):CREATE USER 'dev_ro'@'192.168.10.%' IDENTIFIED WITH mysql_native_password BY 'StrongPass2026!'; - 再授连接权:
GRANT USAGE ON *.* TO 'dev_ro'@'192.168.10.%'; - 最后授业务库只读权:
GRANT SELECT ON `myapp_db`.* TO 'dev_ro'@'192.168.10.%';
执行完无需 FLUSH PRIVILEGES(除非你手动改过 mysql.user 表),GRANT 自动生效。
开发者常需要 SHOW CREATE TABLE,得额外授权 information_schema
ORM 初始化、迁移脚本、DBA 查表结构时,常执行 SHOW CREATE TABLE t1。这条语句底层依赖对 information_schema.TABLES 和 information_schema.COLUMNS 的 SELECT 权限。MySQL 8.0+ 默认收紧该访问,仅授业务库权限时,这条命令会报 ERROR 1142 (42000): SELECT command denied。
解决方法很简单,加一行:
GRANT SELECT ON `information_schema`.`TABLES` TO 'dev_ro'@'192.168.10.%';- 如果还要看列定义,补上:
GRANT SELECT ON `information_schema`.`COLUMNS` TO 'dev_ro'@'192.168.10.%';
注意:不需要整个 information_schema 库,只授这两个表即可,最小化暴露面。
MySQL 8.0 推荐用角色封装权限,但 DEFAULT ROLE 必须显式设置
直接对每个用户重复 GRANT 容易出错、难回收。8.0+ 更推荐用角色统一管理:
- 创建角色:
CREATE ROLE 'dev_readonly_role'; - 授业务库权:
GRANT SELECT ON `myapp_db`.* TO 'dev_readonly_role'; - 授元数据权:
GRANT SELECT ON `information_schema`.`TABLES` TO 'dev_readonly_role'; - 绑定用户:
GRANT 'dev_readonly_role' TO 'dev_ro'@'192.168.10.%'; - 启用默认角色(关键!):
SET DEFAULT ROLE 'dev_readonly_role' TO 'dev_ro'@'192.168.10.%';
没有最后这步,用户登录后权限不会自动激活——8.0.16+ 才支持 DEFAULT ROLE 自动生效,低于该版本需用户手动执行 SET ROLE,生产环境别跳过。
真正容易被忽略的点是:角色本身不带 USAGE,所以仍需单独给用户授 USAGE ON *.*;另外,角色不能继承新创建的库权限,后续新增库必须显式追加授权。


















