行,但需满足租户间无共享数据、不共用连接池、应用层严格控制库名拼接;须严控CREATE/DROP SCHEMA权限,授权必须用GRANT ... ON schema_name.*,禁用动态库名拼接,且生命周期管理需人工审批。

MySQL 多租户用 Schema 隔离行不行?
行,但前提是租户间**完全无共享数据需求、不共用连接池、应用层能严格控制库名拼接逻辑**。Schema 级隔离本质是“物理分离”,不是权限魔法——CREATE SCHEMA 和 DROP SCHEMA 权限必须收严,否则一个租户建库就能撞进别人地盘。
常见错误现象:Access denied for user 'app'@'%' to database 'tenant_b',其实是应用连错了库,或没显式指定 USE tenant_a;更隐蔽的是 ORM 自动 fallback 到默认库,查出其他租户数据。
- 每个租户对应唯一
schema_name(如tenant_123),禁止用下划线以外的符号,避免 SQL 拼接注入 - 应用启动时必须校验当前连接的
database()返回值是否匹配预期schema_name - 不要依赖
mysql_select_db()动态切换——连接复用时状态残留,容易串库
怎么给租户账号只开 Schema 级权限?
用 GRANT ... ON schema_name.*,不是 GRANT ... ON *.*。漏掉末尾的 .* 会导致权限不生效,这是最常踩的坑。
示例:给租户账号 tenant_456 授权:
GRANT SELECT, INSERT, UPDATE, DELETE ON `tenant_456`.* TO 'tenant_456'@'%'; FLUSH PRIVILEGES;
注意点:
-
schema_name必须用反引号包裹,尤其含数字或短横线时(如`tenant-789`) - 不能授
CREATE或DROP权限,否则租户可删自己库甚至越权建库 - 如果用 MySQL 8.0+,建议配合角色(
CREATE ROLE)管理,避免直接授给用户
为什么不能只靠 GRANT 就算完事?
因为 MySQL 的权限检查发生在语句解析后、执行前,而 Schema 名若来自用户输入且未校验,SELECT * FROM tenant_x.users 里的 tenant_x 可能被替换成别的库名——权限系统根本不会拦,它只认最终解析出的库表名。
所以关键在应用层:
- 所有 SQL 中的库名必须硬编码或从白名单配置读取,禁用运行时拼接
- 连接字符串里必须带
?database=tenant_123参数(如 JDBC 的useSSL=false&database=tenant_123) - ORM 如 MyBatis 的
<databaseId>或 Hibernate 的spring.jpa.properties.hibernate.default_schema要设死,别留空
Schema 隔离在哪些场景会翻车?
跨租户统计、全局字典表、审计日志合并——这些需求一出现,Schema 隔离就撑不住。强行用 UNION ALL 查多个 tenant_*.log 表,性能差、维护难、权限还得额外开。
还有两个隐形雷:
- 备份恢复:mysqldump 默认不带
--databases,单库备份脚本可能漏掉新租户库 - 监控告警:Prometheus 的
mysql_exporter若只盯information_schema.TABLES,新 Schema 不入库就看不到增长
真正麻烦的从来不是授权命令怎么写,而是租户生命周期和数据库对象生命周期怎么对齐——删租户时,DROP SCHEMA 得进审批流,不能由应用自动触发。










