只读用户连不上数据库主因是host匹配失败和authentication plugin不兼容;需检查IP网段、指定mysql_native_password插件、授权information_schema.columns、限制账号资源并处理连接池缓存。

只读用户连不上数据库:host 和 authentication plugin 两个坑最常见
新建的 SELECT 权限账号连不上外部报表工具,大概率卡在连接层,不是权限没给够,而是 MySQL 拒绝了认证请求。常见于用 Navicat、Tableau 或 Python 的 pymysql 连接时抛出 Access denied for user 或 Client does not support authentication protocol。
- 检查
CREATE USER语句里是否显式指定了HOST,比如'reporter'@'192.168.10.%'—— 如果报表服务器 IP 不在这个网段,或者用了 NAT 后的真实出口 IP,就直接拒绝 - MySQL 8.0+ 默认用
caching_sha2_password插件,但旧版客户端(如某些 Java JDBC 驱动、老版本 Excel Power Query)不支持;建用户时得加IDENTIFIED WITH mysql_native_password BY 'xxx' - 确认
skip-name-resolve已在 my.cnf 中启用,否则反向 DNS 查询失败也会导致连接超时或拒绝
GRANT SELECT 够不够?要看表结构变更和视图依赖
GRANT SELECT ON db_name.* TO 'reporter'@'%' 看似完整,但外部报表系统常会自动探测元数据——它可能执行 SHOW CREATE TABLE、查 INFORMATION_SCHEMA.COLUMNS,甚至依赖视图。这些操作默认不包含在 SELECT 权限里。
- 如果报表工具报错含
access denied to database 'information_schema',需单独授权:GRANT SELECT ON INFORMATION_SCHEMA.COLUMNS TO 'reporter'@'%'(最小化只需COLUMNS表,不用整个库) - 用视图做数据口径统一?必须确保该用户对视图底层所有基表都有
SELECT权限,且创建视图时用了SQL SECURITY DEFINER(否则权限以调用者为准,而 reporter 没有基表权限) - 避免给
GRANT OPTION,哪怕只读用户也不需要——它会让用户能把权限转授他人,是安全红线
报表查询拖慢线上库?限制并发和资源比单纯加索引更有效
只读用户不等于“无害”。一个复杂报表 SQL 可能扫描千万行、占满 buffer pool、触发大量磁盘 IO,间接拖垮业务写入。MySQL 原生不提供查询级限流,得靠账号级资源控制兜底。
- 建用户时加上资源限制:
MAX_QUERIES_PER_HOUR 1000 MAX_CONNECTIONS_PER_HOUR 20 MAX_USER_CONNECTIONS 5,防止单个账号发起密集轮询 - 对关键报表 SQL,建议在应用侧加
/*+ MAX_EXECUTION_TIME(30000) */提示(MySQL 5.7.8+ 支持),超时自动 kill,避免长查询堆积 - 不要依赖
read_only=ON全局开关来“保护”只读账号——它只阻止 DML,不影响SELECT ... INTO OUTFILE或锁表操作,且一旦误关就全库可写
权限回收后仍能查到数据:缓存、代理与连接池的隐性影响
执行 DROP USER 或 REVOKE 后,报表系统可能还在用旧连接继续查数据,不是权限没生效,而是连接没断。
- MySQL 权限变更不会自动踢掉已存在的连接,需手动
KILL对应线程,或等连接空闲超时(由wait_timeout控制,默认 8 小时) - 如果中间用了 ProxySQL 或 MaxScale,权限策略可能在代理层缓存,得同步刷新代理的用户表或重启服务
- Java 应用常用 HikariCP 这类连接池,池子里的连接复用会导致权限变更延迟生效,建议设置较短的
maxLifetime(如 1800000ms)并配合testOnBorrow
权限配置本身不难,难的是想清楚“谁在什么路径下用什么方式访问”,漏掉 DNS、插件、元数据、连接复用中任意一环,都会让只读账号变成不可控变量。


















