答案是权限在连接建立时固化为会话快照,flush privileges仅刷新新连接的全局acl缓存,不影响已存在连接;表级权限热生效,数据库级需use触发,全局级必须重连。

权限在连接建立时就固化进会话内存
MySQL 不会在连接存活期间反复检查磁盘上的权限表。每个会话启动时,mysqld 从 mysql.user、mysql.db 等系统表读取匹配的权限规则,构建一份只读快照存入该会话的内存中。后续所有 SQL 执行(如 SELECT、INSERT)都直接查这份快照,不触磁盘、不重载、不轮询。
FLUSH PRIVILEGES 只刷新服务端全局缓存,不触碰已有会话
FLUSH PRIVILEGES 的作用是强制 mysqld 重新读取磁盘权限表,覆盖内存中用于**新连接认证**的 ACL 缓存(比如 ACL_USER 数组)。但它对已存在的连接毫无影响——那些会话的权限快照早已独立存在,和全局缓存无关。
- 执行
GRANT DELETE ON mydb.* TO 'u'@'h';后,当前连接仍报ERROR 1142 (42000): DELETE command denied - 执行
FLUSH PRIVILEGES后,同一连接继续报错 - 只有退出客户端再
mysql -u u -p重连,新会话才能拿到DELETE权限
连接池场景下“长连接”让问题更隐蔽
应用层用 HikariCP、Druid 等连接池时,一个连接可能复用数小时。即使你改了权限并 FLUSH PRIVILEGES,只要池没主动淘汰旧连接,它就永远带着旧权限快照运行。
- HikariCP 默认不校验连接有效性,
connection-init-sql需手动设为SELECT 1才能在取连接时触发失败重建 - Druid 需配
testWhileIdle=true+timeBetweenEvictionRunsMillis=30000才能每 30 秒清理失效连接 - 别指望
FLUSH PRIVILEGES让池里老连接“突然生效”,它根本做不到
不同粒度权限的生效时机差异极大
不是所有权限都等重连。MySQL 对权限做了分层加载,生效时机取决于你授的是哪一级:
-
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';→ 必须重连(全局级,只影响新会话)
真正容易被忽略的,是权限匹配本身:你 GRANT 的是 'u'@'localhost',但应用连的是 'u'@'127.0.0.1',这两个账号完全独立;或者 MySQL 8.0+ 用了 caching_sha2_password 插件,而客户端驱动太旧,连得上却权限静默失效——这些都比“要不要刷缓存”更优先排查。











