分库分表后权限不一致的根本原因是各实例独立维护mysql系统表,权限不自动同步;必须通过声明式定义+Ansible等自动化工具逐节点校验并修复,或统一收口至带审计的授权平台。

分库分表后权限分散导致“同一用户不同节点权限不一致”
分库分表本身不改变 MySQL 的权限存储机制——每个实例仍独立维护自己的 mysql.user、mysql.db 等系统表。这意味着:你在分片 A 上执行 GRANT SELECT ON order_001 TO 'app'@'10.20.%',分片 B 完全不知道这事。一旦应用按路由规则连到 B,就会直接报 Access denied。这不是 SQL 写错了,是权限根本没存在那个节点上。
更隐蔽的问题是:开发可能只在主库(或某个测试分片)配了权限,上线后流量打到其他分片,权限缺失却无日志报警;或者 DBA 在某节点临时加了 DROP 权限调试,忘了同步,变成生产隐患。
手动逐节点执行 GRANT 语句不可靠且易出错
有人想“那就写个脚本循环所有分片执行一遍 GRANT”,但实际会撞上几个硬坑:
-
CREATE USER IF NOT EXISTS在 5.7 和 8.0+ 行为不一致:8.0 默认用caching_sha2_password插件,若某节点还停在mysql_native_password,即使用户存在也会认证失败 - 主机名匹配失效:
'app'@'%'在 DNS 解析慢的节点可能被解析成空字符串,而'app'@'10.20.%'才真正稳定 - 角色(ROLE)无法跨实例自动传播:MySQL 8.0 的
CREATE ROLE是本地操作,GRANT role TO user不触发复制,必须每个实例单独建角色再绑定 - 列级权限、动态权限(如
BACKUP_ADMIN)用SHOW GRANTS导出时默认不显示,脚本一跑就漏
中间件(如 ProxySQL)做权限抽象只是掩耳盗铃
把权限逻辑搬到 ProxySQL 的 mysql_users 表里,看似统一了入口,但本质没解决问题:
运维直连某个 MySQL 分片排查问题时,mysql -h shard-b -u app 仍然走的是该节点本地的 mysql.user,ProxySQL 的配置完全不生效;而 mysql_users 表本身又得手动同步到每个 ProxySQL 实例——你只是把“同步 MySQL 权限”的工作,换成了“同步 ProxySQL 权限”,还多了一层组件依赖。
更关键的是:mysql_users 只控制连接鉴权,不控制 SQL 执行权限。用户连进去了,照样可以执行 DROP TABLE,只要那个分片上没配 REVOKE DROP ON *.*。
真正能落地的统一权限管理只有两条路
第一是放弃“实时同步”,改用声明式权限定义 + 自动化部署:
- 用 YAML 或 JSON 描述每个角色应有权限(例如
role: analyst, grants: ["SELECT ON sales_db.*"]) - 通过 Ansible / SaltStack 调用
pt-show-grants --no-create-user --columns校验各节点权限是否与定义一致,不一致则自动修复 - 所有权限变更必须走 Git 提交 + CI 触发部署,禁止手工登录任意节点执行
GRANT
第二是彻底收口权限入口,只允许通过带审计的平台下发授权语句——但这要求所有 DBA 和开发都绕过命令行,接受流程约束。实践中最难的从来不是技术方案,而是谁来保证每次新建账号、每次调整权限,都严格走这个流程。


















