权限变更后连接池旧连接仍报access denied,因mysql权限检查仅在连接建立时生效;需配置连接池主动校验(如hikaricp设connection-init-sql=select 1)或定期淘汰空闲连接(如druid配testwhileidle=true),而非依赖flush privileges。

权限变更后连接池里老连接仍报 Access denied
权限改了,但连接池里的活跃连接还在用旧权限缓存,执行 SQL 时直接报 Access denied——这不是权限没加对,而是 MySQL 权限检查只在连接建立那一刻生效,后续不会动态更新。连接池里的连接一旦建立,就“锁定”了当时的权限快照。
- 必须让连接池主动淘汰旧连接,而不是等它自然超时;否则可能持续数小时都在失败
- HikariCP 可设
connection-init-sql=SELECT 1,每次从池取连接时先执行一次校验,失败则自动丢弃并新建 - Druid 推荐配
testWhileIdle=true+validationQuery=SELECT 1+timeBetweenEvictionRunsMillis=30000,每 30 秒扫一遍空闲连接是否还可用 - 别依赖
FLUSH PRIVILEGES:它不影响已有连接,加了也没用,反而掩盖问题根源
GRANT 后权限不生效,但 SHOW GRANTS 显示正常
SHOW GRANTS FOR 'u'@'h' 只查内存/磁盘的授权记录,不代表当前连接能用。尤其要注意权限粒度:
-
GRANT SELECT ON db1.t1 TO 'u'@'h'→ 下一条SELECT * FROM t1就生效(表级,热更新) -
GRANT SELECT ON db1.* TO 'u'@'h'→ 得先执行USE db1才触发刷新(数据库级) -
GRANT SELECT ON *.* TO 'u'@'h'→ 必须重连(全局级,只影响新连接) - 如果应用用的是连接池,且没配置
connection-init-sql,那它永远拿不到全局权限
MySQL 8.0+ 用户认证插件不匹配导致静默权限失效
权限明明给了,也能连上,但一执行 DML 就失败——大概率是 caching_sha2_password 插件和老客户端(如旧版 Navicat、Python MySQLdb、某些 JDBC 驱动)不兼容。连接成功,但权限校验阶段被跳过或降级失败。
- 查真实插件:运行
SELECT User, Host, plugin FROM mysql.user WHERE User = 'u'; - 临时兼容方案:
ALTER USER 'u'@'h' IDENTIFIED WITH mysql_native_password BY 'pwd'; - 长期建议升级客户端驱动,比如 JDBC 必须用
mysql-connector-java 8.0.23+,URL 加allowPublicKeyRetrieval=true - 云数据库(如阿里云 RDS)通常禁用
FLUSH PRIVILEGES,执行直接报ERROR 1227,别白费力气
迁移或批量修复后权限表结构异常
从 5.7 升级到 8.0 后手动改过 mysql.user,或用 mysqldump 导入旧权限数据,极易因字段缺失或格式错误导致 GRANT 写不进底层。
- 先确认表结构完整:
DESC mysql.user;看有没有authentication_string、account_locked、password_expired等 8.0 字段 - 若
plugin是空或'',说明认证插件未设置,ALTER USER比直接 UPDATE 安全得多 - 避免直接
UPDATE mysql.user SET authentication_string = PASSWORD('x'):8.0 已废弃PASSWORD()函数,会写入无效哈希 - 真正需要
FLUSH PRIVILEGES的场景极少:仅当你绕过 GRANT、用 INSERT/UPDATE 直接操作系统表后才必须执行
生产环境最易忽略的一点:连接池里的连接不是“活”的,而是“僵住的”。权限变了,它不会自己醒过来。你得逼它换血,而不是等它断气。











