grant select 是创建只读账号的正确方式,即仅授予 select 权限,而非禁用写权限;必须指定数据库和表范围,需额外授权 information_schema 才能执行 show create table;create user 和 grant 必须分两步;新账号需有 usage 权限才能登录;mysql 8.0 可用角色管理但不自动继承新库权限;视图/函数须设 sql security invoker 防绕过。

GRANT SELECT 是创建只读账号的正确方式
MySQL 里没有 READ ONLY 用户类型,所谓“只读账号”本质就是只授予 SELECT 权限的账号。直接用 GRANT SELECT 是最稳妥的做法,比试图禁用写权限(比如 REVOKE INSERT, UPDATE...)更可靠——权限是白名单机制,没给的就是不能做。
常见错误是先创建用户再反复 REVOKE,结果漏掉某个权限(比如忘了 TRUNCATE 或 LOCK TABLES),反而留了后门。只授 SELECT 一劳永逸。
-
GRANT SELECT必须指定数据库和表范围,GRANT SELECT ON *.*是全局只读,GRANT SELECT ON mydb.*是库级只读 - 如果用户需要查
INFORMATION_SCHEMA(比如看表结构),得额外加GRANT SELECT ON INFORMATION_SCHEMA.*,否则SHOW CREATE TABLE会失败 - MySQL 8.0+ 默认启用
sql_require_primary_key等安全选项,但不影响只读账号行为;不过若应用执行SELECT ... FOR UPDATE,即使只有SELECT权限也会报错:ERROR 1142 (42000): SELECT command denied to user—— 因为FOR UPDATE触发了锁机制,需SELECT+LOCK TABLES权限
CREATE USER 和 GRANT 要分两步写,别合在一条语句里
MySQL 5.7 及以后不支持在 CREATE USER 时直接带 GRANT,强行写成一句会报错:ERROR 1064 (42000): You have an error in your SQL syntax。必须拆开:
CREATE USER 'ro_user'@'%' IDENTIFIED BY 'strong-pass-2024';<br>GRANT SELECT ON myapp_db.* TO 'ro_user'@'%';
注意点:
- 主机名别用
'%'直接放行所有 IP,生产环境建议限定内网段,比如'192.168.10.%' - 密码必须用单引号包裹,且不能是纯数字或简单字符串;MySQL 8.0+ 默认校验密码强度,太弱会拒绝创建
- 执行完
GRANT后无需FLUSH PRIVILEGES(除非你手动改过mysql.user表),GRANT自动刷新权限缓存
只读账号连不上?检查是否漏了 USAGE 权限
新建账号即使只有 SELECT,也必须有 USAGE 权限才能登录。而 GRANT SELECT 不自动包含 USAGE —— 它只是连接权,不是操作权。漏掉会导致:ERROR 1045 (28000): Access denied for user 'ro_user'@'192.168.1.100',哪怕密码正确。
解决方法很简单,补上:
GRANT USAGE ON *.* TO 'ro_user'@'%';
但更常用的是直接在 GRANT SELECT 后加 WITH GRANT OPTION?不,千万别。那是给其他账号授予权限的权力,只读账号绝对不能有。只要确保 USAGE 存在即可,它默认就附带在任何 GRANT 语句中——前提是语句里没写错范围(比如写成 GRANT SELECT ON *.* 却忘了 USAGE,其实 MySQL 会隐式加上;但若只写 GRANT SELECT ON mydb.t1,则 USAGE 不生效,必须显式补)。
MySQL 8.0 的角色机制让只读管理更清晰,但别滥用
MySQL 8.0 支持 CREATE ROLE,你可以建一个 role_readonly,把 SELECT 权限全授给它,再把用户加进角色。好处是权限变更只需改角色,不用逐个用户操作。
但要注意:
- 角色本身不能登录,必须
SET DEFAULT ROLE给用户,否则用户登录后权限不生效 - 角色权限不会自动继承到新库:新建库
newdb后,role_readonly对它没权限,得手动GRANT SELECT ON newdb.* TO role_readonly - 如果用
mysqldump --single-transaction备份,只读账号能跑,但若备份脚本里含SHOW MASTER STATUS,就得额外给REPLICATION CLIENT权限——这不是只读,是复制相关,别混为一谈
真正容易被忽略的是:只读账号在视图(VIEW)或存储函数里访问底层表时,权限检查发生在定义者(DEFINER)上下文。如果视图用 DEFINER = 'root'@'localhost',那用户查视图时实际走的是 root 权限——等于绕过了只读限制。所以务必确认视图和函数的 SQL SECURITY 设为 INVOKER。











