分库分表后mysql账户权限不自动跨库生效,因mysql实例不感知逻辑分库,各物理库权限独立;须改用全局最小权限(如grant select,insert,update,delete on .)或角色封装,并由中间件/应用层实现租户级数据隔离。

分库分表后,MySQL账户权限无法自动跨库生效
分库分表只是应用层或中间件(如ShardingSphere、MyCat)做的逻辑拆分,MySQL 实例本身并不感知「分库」概念。每个物理库仍是独立的 database,权限仍按 'user'@'host' + 权限层级(全局 / 库级 / 表级)加载。所以给 app_db1.* 授了 SELECT,对 app_db2.* 完全无效——这是最常被忽略的前提。
统一管控必须放弃「按库授权」,改用最小化全局权限
如果你有 20 个分库(app_db_00~app_db_19),逐个执行 GRANT SELECT ON app_db_XX.* TO 'app_user'@'%' 不仅运维不可持续,还极易漏配、版本不一致。正确做法是:
- 只授全局级最小权限:
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'app_user'@'%' WITH GRANT OPTION—— 注意:不是ALL PRIVILEGES,且WITH GRANT OPTION仅在需要中间件代理建表时才加 - 用
REVOKE显式禁掉高危权限:REVOKE FILE, PROCESS, SUPER, SHUTDOWN ON *.* FROM 'app_user'@'%' - 配合 MySQL 8.0+ 的角色机制,把上述权限封装成角色:
CREATE ROLE 'app_dml'; GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'app_dml'; GRANT 'app_dml' TO 'app_user'@'%',后续增删库无需改权限,只管建库即可
真正要防的不是「能连哪些库」,而是「能查哪些数据」
分库分表场景下,权限失控风险主要来自业务逻辑绕过(比如用户A本该只查 app_db_01,但构造请求直连 app_db_02)。这时靠 MySQL 账户权限已无意义,必须由上层控制:
- 所有数据库连接统一走中间件或代理层,禁止应用直连物理实例;中间件按租户/用户ID路由,并校验目标库是否在白名单内(如 ShardingSphere 的
db-policy) - 敏感字段(如用户手机号、身份证)必须加密存储,且应用层做列级脱敏——MySQL 的列级权限(
GRANT SELECT(col1) ON db.t1)在分库后难以维护,且不防 SQL 注入绕过 - 若用 MySQL 8.0.25+ 且强依赖 DB 层拦截,可为每个分库创建同名视图(
CREATE VIEW app_db_01.users AS SELECT id, name, email FROM app_db_01.users WHERE tenant_id = @current_tenant),再授视图权限;但注意@current_tenant需由应用 SET,且不能依赖连接池复用(会串值)
权限备份与同步别碰 mysql 库,用 SHOW GRANTS + 角色导出
有人试图 mysqldump mysql 后导入到其他分库实例,结果导致新实例启动失败或用户登录报 Access denied——因为 mysql.plugin、mysql.help_topic 等表与实例强绑定,且密码哈希方式(caching_sha2_password vs mysql_native_password)跨版本不兼容。
- 统一导出用:
SHOW GRANTS FOR 'app_user'@'%',再人工替换库名为通配符(*.*) - 角色权限导出更可靠:
SELECT CONCAT('GRANT ', privilege, ' ON ', IF(is_grantable = 'YES', 'WITH GRANT OPTION;', ';')) FROM role_edges re JOIN role_routine_grants rr ON re.role_id = rr.role_id WHERE re.role_name = 'app_dml'(需结合information_schema查询) - 每次新增分库后,只需确认该库存在、且
app_user能连上——权限已由*.*或角色覆盖,无需额外操作
分库分表下的权限管控,本质是把「库级隔离」让渡给中间件或应用层,MySQL 只守好连接入口和 DML 边界。越想在 DB 层做细粒度路由或数据过滤,越容易掉进权限缓存、字符集、连接池变量污染的坑里。











