mysql.user表查得慢是因为权限检查时隐式联查多张权限表且缺乏有效索引,导致全表扫描;MySQL 5.7及以前无缓存,8.0+虽有缓存但仅在FLUSH PRIVILEGES或DDL后重建,非实时更新。

mysql.user 表为什么查得慢?
直接查 mysql.user 表本身不慢,慢的是每次执行权限检查时隐式触发的多表联查和元数据扫描。MySQL 在用户登录、执行语句、访问数据库对象前,会动态拼接查询去 mysql.user、mysql.db、mysql.tables_priv 等表里捞权限,这些表默认没建有效索引,且 InnoDB 引擎下全表扫描开销明显。
常见错误现象:SHOW GRANTS FOR 'u'@'h' 延迟高;新建用户后首次登录卡顿;大量并发连接时 Performance_schema.threads 显示大量线程卡在 checking permissions 状态。
- MySQL 8.0+ 默认用缓存加速权限检查,但缓存只在用户权限变更(如
FLUSH PRIVILEGES或 DDL)后重建,不是实时更新 -
mysql.user表主键是(Host,User),但权限检查常按User单独查,或带通配符 Host(如'%'),导致索引失效 - MySQL 5.7 及更早版本无权限缓存,每次检查都走磁盘表,压力更大
FLUSH PRIVILEGES 到底要不要执行?
绝大多数情况不用——而且不该随便执行。它强制清空权限缓存并重载所有权限表,会阻塞后续所有权限检查请求,造成瞬时雪崩。
使用场景仅限两种:UPDATE/INSERT/DELETE 直接改了 mysql 库下的权限表(绕过 GRANT/REVOKE);或确认缓存已损坏(比如改完权限但行为没变,且确定没用 GRANT)。
- 用
GRANT、REVOKE、CREATE USER等 DDL 修改权限,MySQL 自动更新缓存,无需FLUSH PRIVILEGES - 执行
FLUSH PRIVILEGES后,所有活跃连接的权限不会立即刷新,新连接才生效;已有连接仍用旧缓存,直到下次权限检查点 - MySQL 8.0+ 的缓存结构更复杂,包含角色继承、密码策略等,
FLUSH PRIVILEGES还可能触发额外校验,耗时更长
如何验证权限缓存是否生效?
看 performance_schema 里的计数器最直接。缓存命中高,说明系统没频繁回表查权限表。
SELECT * FROM performance_schema.status_by_thread WHERE VARIABLE_NAME = 'Threads_cached';
更关键的是查这两个指标:
-
Handler_read_rnd_next:如果对mysql.user等表的随机读很高,说明缓存未覆盖或失效频繁 -
Innodb_buffer_pool_read_requestsvsInnodb_buffer_pool_reads:后者占比突增,可能是权限表被反复读取且没进 buffer pool - 启用
log_queries_not_using_indexes = ON,观察慢日志里是否有SELECT ... FROM mysql.user WHERE User=...类语句
权限表能加索引优化吗?
不能手动加。MySQL 内部硬编码了权限表的结构和访问路径,ALTER TABLE mysql.user ADD INDEX 会被拒绝,即使成功也会在重启或 FLUSH PRIVILEGES 后丢失。
真正可控的只有两件事:控制权限粒度、减少权限表变更频次。
- 避免给
'%'@'%'这类宽泛 Host,优先用具体 IP 或域名,让(Host,User)索引能高效命中 - 不用
GRANT ALL ON *.*,细化到库或表级,减少mysql.db、mysql.tables_priv的扫描范围 - 批量创建用户时,用
CREATE USER+GRANT批量提交,而不是逐条执行,降低缓存重建次数
缓存机制本身不暴露配置开关,唯一影响它的就是你的 GRANT 风格和 Host 写法——这点容易被忽略,但实际影响比调参大得多。



















