租户数据隔离必须靠逻辑隔离+字段约束,不能只依赖数据库用户权限;mysql不支持行级租户隔离,需通过应用层自动注入tenant_id、关键表加非空字段与联合索引、禁用高权限账号、单库多租户为主流、备份恢复需支持租户维度快照等综合手段实现。

租户数据隔离必须靠逻辑隔离+字段约束,不能只依赖数据库用户权限
MySQL 原生不支持行级租户隔离,仅靠 CREATE USER + GRANT 只能控制库/表级访问,无法阻止同一应用连接下跨租户查数据。真实风险在于:应用层拼 SQL 时漏加 tenant_id = ?,一条 SELECT * FROM orders 就可能拉出所有租户订单。
- 强制在所有查询中绑定租户上下文,例如用 MyBatis 的
Interceptor自动注入AND tenant_id = #{tenantId} - 关键表(如
orders、users)必须有非空tenant_id字段,并建联合索引(如INDEX idx_tenant_status (tenant_id, status)) - 禁止应用直连 MySQL root 或高权限账号;每个租户对应一个只读/读写账号的做法在多租户场景下运维成本爆炸,不推荐
避免用数据库名或 Schema 名做租户隔离
早期方案常用 tenant_001.orders、tenant_002.orders 这种分库方式,但实际带来严重问题:连接池无法复用、DDL 同步困难、备份恢复粒度粗、跨租户统计几乎不可行。
- 单库多租户是主流选择,前提是业务量可控(单表千万级以内,QPS 不超 5000)
- 若真需分库,应基于路由键(如
tenant_id % 8)做中间件层分片,而不是靠人工建库——MySQL 本身不感知分库逻辑 -
INFORMATION_SCHEMA和SHOW TABLES在分库后失效,运维脚本容易误操作
权限策略要收口到应用网关和 DAO 层,不是 DBA 配置完就结束
DBA 给应用服务分配的 MySQL 账号,应该只有单个数据库的 SELECT, INSERT, UPDATE, DELETE 权限,且禁用 DROP、ALTER、CREATE VIEW 等高危操作。但这只是起点。
- DAO 层必须校验当前请求的
tenant_id是否属于该用户所属租户(防止越权访问自己的子租户) - 敏感操作(如删除租户、导出全量数据)需二次鉴权,记录
audit_log表并触发告警 - 避免在 SQL 中拼接租户 ID:
"WHERE tenant_id = " + userId是典型漏洞,一律改用预编译参数
备份与恢复必须带租户维度快照能力
标准 mysqldump 备份整个库,恢复时无法只还原某个租户的数据。一旦某租户误删数据,传统方案只能回滚全库,影响其他租户。
- 定期执行按租户导出:
mysqldump --where="tenant_id='t-123'" myapp orders users - Binlog 解析需保留
tenant_id上下文(例如在注释里写/* tenant: t-456 */),否则闪回工具无法过滤 - 物理备份(xtrabackup)虽快,但恢复时仍需额外脚本按租户拆分数据,建议搭配逻辑备份做兜底
租户 ID 的透传链路(从 API 请求头 → 微服务上下文 → SQL 参数)最容易断,任何一环没带过去,隔离就失效。别信“我们代码里都写了”,上线前必须用非法 tenant_id 做负向测试。











