根本原因是MySQL主从默认不同步mysql.user表权限,因旧版本不将GRANT等操作写入binlog;需确保binlog开启、不忽略mysql库、使用GRANT而非INSERT,并注意GTID一致性。

MySQL主从同步中,从库查不到用户、连不上、报 Access denied for user,根本原因不是权限没给,而是权限压根没同步过去。
为什么 GRANT 在主库执行后,从库还是没权限
因为 MySQL 主从默认不把 mysql.user 表的变更写进 binlog —— 尤其是 5.7 及更早版本,GRANT 语句默认不记日志;8.0+ 虽默认记录,但前提是没关 log_bin,且没配置 replicate_ignore_db=mysql。
常见错误现象包括:
-
SELECT user,host FROM mysql.user在主库有结果,从库返回空 - 应用连从库时报
Access denied for user 'u'@'h' -
SHOW GRANTS FOR 'u'@'h'在从库返回空或报错ERROR 1141 (42000): There is no such grant defined for user 'u' on host 'h'
检查是否跳过 mysql 库:
SELECT * FROM performance_schema.replication_applier_configuration;
或看 SHOW SLAVE STATUS\G 中的 Replicate_Ignore_DB 字段是否含 mysql。
必须用 GRANT(而非 INSERT INTO mysql.user)同步权限
直接操作 mysql.user 表不仅不触发权限重载,还极大概率导致 binlog 记录不完整或格式错误,从库回放失败。
GRANT 是原子性 DDL,在支持的版本中能正确生成 binlog 事件:
CREATE USER 'reporter'@'%' IDENTIFIED BY 'pwd';GRANT SELECT ON db.* TO 'reporter'@'%';- 执行完无需手动
FLUSH PRIVILEGES(GRANT自动重载),但从库若延迟大,可酌情执行一次确保内存权限即时生效
避免以下写法:
INSERT INTO mysql.user (Host,User,authentication_string) VALUES ('%','u',PASSWORD('p'));
字段顺序、密码加密方式(authentication_string vs password)、密码过期策略等极易出错,且不保证 binlog 正确。
MySQL 8.0+ 角色与 GTID 下的权限同步注意事项
8.0+ 的 CREATE ROLE 和角色授权也走 binlog,但要求主从都为 8.0+,否则从库因无法识别语法中断复制。
GTID 模式下,权限变更事务必须被完整记录和回放;若从库报错如 ERROR 3547 (HY000)(“Role does not exist”),说明该角色在从库缺失,不能靠跳过解决,而应检查主库是否已执行对应 CREATE ROLE 并确认 binlog 已传到从库。
关键配置检查项:
- 主库:确认
log_bin=ON,binlog_format=ROW或MIXED,log_bin_trust_function_creators=1(5.7 必须) - 从库:
replicate_ignore_db不含mysql,read_only=ON(防误写) - 不要用
mysqldump --databases mysql导入从库——会导致自增 ID、password_last_changed等内部字段冲突,让后续权限变更直接失败
权限不一致时,别直接在从库补 GRANT
有人发现从库缺用户,就手动在从库跑一遍 GRANT。这看似快,实则埋雷:主库后续再执行同名用户权限变更(比如加 INSERT),从库会因用户已存在而报错 ERROR 1396 (HY000),复制中断。
真正可靠的路径只有一条:确保主库的权限操作全部进 binlog,并被从库正常回放。一旦发现不一致,优先排查 binlog 开关、过滤配置、GTID 设置,而不是绕过机制人工干预。
最常被忽略的点是 replicate_ignore_db=mysql —— 它像一道静默的墙,把所有权限变更挡在从库门外,却不会报错,只会让你反复怀疑是不是 GRANT 写错了。











