客户A登录后刷出了客户B的订单,共库加租户字段能马上堵住吗?
结论先说:多租户串数据通常不是数据库自己出错,而是租户上下文在某个入口丢了。按 2026 年常见交付经验,租户数在几十到几百、单租户数据量不大时,共库共表加租户 ID 列是常见起点,但要同时配三道防线:服务端绑定租户上下文、数据访问层强制拼接租户条件、库层用带租户 ID 的唯一索引兜底。少一道,串数据风险就会明显上升。出现合同或监管要求物理隔离、单租户数据量明显高于其他租户、或需要按租户单独恢复时,再往独立 schema 或独立库走。
串数据多数不是数据库的锅
数据库只执行你给出的语句。语句里没有租户条件,它就会把整张表返回;问题出在写语句的人和生成语句的封装层。漏点分散在不同模块,演示环境数据少时不容易暴露,往往等两个租户数据攒起来、名称又相近才被客户截图发现。
- 查询漏带租户条件:新报表 SQL、临时导出语句较容易赶工跳过。
- 写入没写租户 ID:批量导入、手工补数据、后台新建记录时,租户列被留空或带上一个租户的值。
- 唯一约束没带租户 ID:手机号、编码等全局唯一约束,会让 A 占用 B 的名额,表现为“没人用却提示重复”。
- 缓存或会话 key 没带租户:字典、配置、权限缓存被另一租户覆盖,A 登录后看到 B 的配置。
- 异步任务没切换上下文:定时跑批、消息消费复用线程时残留上一个租户的上下文,事后较难复盘。
先加租户字段还是拆库:三档方案怎么比
把隔离形态按三档来切,是因为改造成本与隔离强度基本同向变化。下面的周期是经验区间,实际取决于历史数据规模、模块耦合度和是否要同时改发布流程。
- 共库共表 + 租户 ID 列:改造经验区间多在 1~3 周。隔离强度依赖应用层,较容易漏条件;适合租户多、单租户数据量小、暂无物理隔离要求的场景。
- 共库独立 schema:改造常见区间 1~3 个月。靠 schema 边界降低误操作概率,连接数和迁移脚本数量随租户数增长;适合租户数量可控、希望按租户做权限隔离的场景。
- 独立库或独立实例:隔离强度较高,可按租户单独备份和恢复;运维、监控、发版都要按租户维度拆分,租户越多边际成本越高;适合有合同或监管要求、或单租户数据量明显偏大的场景。
选择顺序按三件事排:有没有明确的物理隔离要求;单租户数据量与峰值是否显著高于其他租户;出问题后能否接受按租户单独恢复。三问里只要有一条答案是“需要”,就值得从共库往独立形态走一步。现实中也可混合:把数据量大或有单独合规要求的租户放到独立库,其余仍共享;路由规则统一收在数据源层,跨租户统计走离线汇总。
真正兜底的是三层:上下文、访问层、库层
按这三层分,是因为它们防的是三类不同问题:第一层防内部人员越权,第二层防开发写漏,第三层防前两层同时失效。只做其中一层,都会留下可复现漏洞。
- 第一层,租户上下文只从服务端取。登录成功后把租户 ID 写进会话或令牌,业务代码不把请求参数、请求头里的租户 ID 当取数依据;参数里的值只做一致性校验,对不上就拒绝。
- 第二层,数据访问层强制拼接。在 ORM 拦截器或统一查询封装中自动追加租户条件,并保留一个需要审批的绕过开关;绕过时记录操作人、租户和原因,便于事后核对。
- 第三层,库层兜底约束。把唯一索引改成带租户 ID 的联合唯一;跨租户强外键尽量不建,或只做弱校验。合规要求高的场景,可评估数据库自带的行级安全策略,但要先确认版本与运维能力支持,不要只写在方案里。
判断做没做到位,有个简单口径:随机抽五个查询接口,去掉业务代码里的租户条件,看是否仍然只返回当前租户数据。去掉就串,说明拦截层没有真正兜住。
一段交付现场经验:在一个多组织后台改造里,常见约束是历史数据几百万行、要求两三周上线,预算不支持一上来拆库。做法是先共库加租户 ID,在数据访问层强制拼接,并补一轮跨租户越权回归。结果是按期上线,代价是回归阶段多花约一周;如果省掉这轮测试,后续靠客户反馈发现串数据,返工量通常更大。
适用与不适用边界
适用:SaaS 产品、多分公司或多门店共用一套后台、多品牌共用系统且数据不能互看。判定核心是存在两个及以上客户,他们的数据本就不应互相可见。
不适用:租户其实是同一公司内部部门,本质是数据权限问题,用角色加数据范围更合适;硬上租户隔离会让公共字典、跨部门审批变得别扭。系统只有一个使用方、数据本就应互相可见,也无需提前引入租户维度。
常见问题
租户 ID 能不能让前端传过来?
不建议。前端参数可篡改,租户 ID 应由服务端在登录态里绑定;参数里的值只做一致性比对,不匹配就直接拒绝请求。
缓存 key 不带租户会出什么事?
同一个 key 会被后访问的租户覆盖,出现 A 看到 B 的配置或权限。常见做法是 key 统一加租户前缀,并给上下文设置合理超时。
共库共享表能不能过合规检查?
常见可以,前提是访问有审计、租户间隔离有可核对证据。若合同或监管明确要求物理隔离,共享表就不满足,需要按租户独立库或独立实例。
只有一个大客户要求独立部署,要不要改整体架构?
不必全拆。混合模式更常见:把该租户单独放独立库或独立实例,其余仍共享,代价是路由和发布流程要能按租户区分。
如果正卡在这一步,建议先做一件事:把现有接口随机抽五条,去掉代码里的租户条件跑一遍,看会不会返回别人的数据。会串的,先补访问层拦截和带租户 ID 的唯一索引,再谈拆库;不会串的,说明拦截层已经起作用,可以按数据量和合规要求慢慢往独立形态演进。租户数少于几十、单租户数据量不大的系统,通常不必急着拆。
-
运营后台点了封禁,用户 App 里还能接着下单,JWT 为什么踢不掉?
日期:2026年9月20日 阅读:39
-
订单号到前端尾数变 0,只让前端用 BigInt 接不改接口行不行?
日期:2026年9月19日 阅读:92
-
库存扣减先查再改老是超卖,先加锁还是先把校验和写入并成一条语句?
日期:2026年9月18日 阅读:77
-
联合索引建了,查询只带后一列还是全表扫描,要再补单列索引吗?
日期:2026年9月16日 阅读:63
-
缓存过期时间都设成一样的,凌晨数据库突然被打满,是不是 key 同时失效闹的?
日期:2026年9月15日 阅读:107




