grant select必须写database_name.而非仅database_name,因mysql不支持直接授予数据库对象select权限;漏掉.会静默失败,且.授权极危险,应逐库显式授权并注意performance_schema等系统表暴露风险。

创建只读账户时,GRANT SELECT 为什么不能只写数据库名?
MySQL 的 GRANT SELECT 权限必须明确作用范围,只写 database_name.* 是合法的,但若漏掉末尾的 .*(比如写成 database_name),命令会静默失败——不报错,也不生效。这是因为 MySQL 把不带通配符的数据库名当作“数据库级对象名”,而 SELECT 权限不支持直接授予数据库对象本身,只支持表(db.table)或库下所有表(db.*)。
实操建议:
- 始终用
GRANT SELECT ON database_name.* TO 'user'@'host';,别省略.* - 如果只想限制到某些表,写成
GRANT SELECT ON database_name.table1 TO ...、GRANT SELECT ON database_name.table2 TO ...,不能合并成一条语句授多个表 - 执行后必须跟
FLUSH PRIVILEGES;,否则权限不会立即生效(尤其在非 super 用户修改后)
只读账户连不上?检查 host 匹配和 authentication plugin
常见错误现象是:用户创建成功、权限也 GRANT 了,但客户端连接时报 Access denied for user 'ro_user'@'192.168.1.100'。这通常不是权限问题,而是 host 字段没对上,或者 MySQL 8.0+ 默认用了 caching_sha2_password 插件,而旧客户端不支持。
实操建议:
- 创建时显式指定 host:
CREATE USER 'ro_user'@'192.168.1.%' IDENTIFIED BY 'pwd';,避免依赖默认的%(可能被防火墙或网络策略拦截) - MySQL 8.0+ 若需兼容老客户端,建用户时加
IDENTIFIED WITH mysql_native_password BY 'pwd' - 确认用户实际从哪个 IP 连入:在客户端执行
SELECT USER(), CURRENT_USER();,前者是“你声称是谁”,后者是“MySQL 认为你是谁”,两者不一致就说明 host 匹配失败
GRANT SELECT ON *.* 有风险吗?
可以,但非常危险。GRANT SELECT ON *.* 看似方便,实际会把所有库(包括 mysql、information_schema、performance_schema)的表都开放只读。攻击者能从中提取用户哈希、权限配置、SQL 模式等敏感元数据。
实操建议:
- 永远不要对生产环境用
*.*,哪怕只是临时查问题 - 如果真要跨库只读,逐个显式授权:
GRANT SELECT ON db1.* TO ...、GRANT SELECT ON db2.* TO ... - 注意
information_schema默认对所有用户可读,但部分视图(如PROCESSLIST)需要额外权限,GRANT SELECT ON *.*会意外放开这些
只读账户还能执行哪些“非查询”操作?
SELECT 权限本身不包含事务控制、锁表、DDL 等能力,但仍有几个容易被忽略的“旁路动作”:
实操建议:
- 仍可执行
SHOW CREATE TABLE、SHOW INDEX、EXPLAIN—— 它们依赖表级SELECT权限,不算额外授权 - 可调用某些内置函数,如
NOW()、USER()、DATABASE(),但不能调用写入型函数(如UUID_SHORT()在某些版本会触发内部写) - 不能执行
SELECT ... INTO OUTFILE,除非额外授予FILE权限(极不推荐) - MySQL 8.0+ 中,即使只有
SELECT,也能通过SELECT * FROM performance_schema.events_statements_current看到其他用户的 SQL(需确认performance_schema是否启用且未禁用相关表)
真正严格的只读,不只是关掉 INSERT/UPDATE/DELETE,还得盯住元数据访问和系统表暴露面。权限颗粒度越细,越容易漏掉一个点。











