缓存键必须以tenant_id为前缀实现租户隔离,如"tenant_123:user:profile:456";须通过工具类封装或AOP统一注入租户ID,避免手动拼接遗漏;分页、异步、跨服务场景需确保租户上下文一致;失效策略应按租户粒度精准清理。

缓存键设计必须把租户标识作为强制组成部分,否则极易发生数据串号——A租户的缓存内容被B租户读到,这不是性能问题,是安全事件。
缓存键必须带租户ID前缀
所有缓存键都应以tenant_id开头,例如:
✅ "tenant_123:user:profile:456"
✅ "tenant_789:order:list:page_1"
❌ "user:profile:456"(无租户上下文,危险)
❌ "order:list:page_1"(全局共享,不可接受)
这种命名方式让缓存层天然具备隔离能力。Redis、Caffeine等主流缓存组件不提供租户维度的访问控制,靠的就是键空间的逻辑分隔。
避免动态拼接导致遗漏
不能依赖业务代码每次手动拼接租户ID,容易漏写或错写。推荐两种落地方式:
- 统一缓存工具类封装:所有
set()/get()方法强制接收tenantId参数,内部自动前置 - 基于AOP的透明增强:在DAO或Service入口处自动提取当前租户上下文,并注入到缓存操作中
比如RuoYi-Plus-Soybean项目就通过TenantContext.getCurrentTenant()配合切面,在缓存调用前完成键名标准化,开发者几乎感知不到租户逻辑。
注意复合场景下的租户一致性
以下情况特别容易出错,需重点检查:
- 分页查询缓存:
"tenant_123:user:list:page_2:size_20"中tenant_123必须与实际查询的租户一致,不能取错上下文 - 异步任务缓存:定时任务或消息消费时,ThreadLocal中的租户上下文可能已丢失,必须显式传递并重建
- 跨服务调用:下游服务若也使用缓存,需将租户ID透传(如通过HTTP Header或RPC上下文),不能只靠本地线程变量
缓存失效策略也要按租户粒度管理
不能全局清空缓存,也不能只按业务类型清理。例如修改租户123的配置后,应只失效:
- "tenant_123:config:*"
- "tenant_123:user:*"(如果配置变更影响用户视图)
Redis不支持通配符批量删除,可用SCAN + DEL组合实现,或改用支持前缀删除的缓存中间件(如RedisJSON或自研代理层)。关键原则是:租户数据的生命周期必须完全独立。


















