[008][租户模块]基于Caffeine的租户隔离与两级缓存实践

简介: 本文介绍基于Caffeine的租户级缓存隔离与两级缓存(Caffeine+Redis)实践,通过`AbstractRoutingCacheManager`实现按租户动态路由、延迟初始化与完全隔离,兼顾高性能与数据安全,适用于SaaS多租户系统。代码开源可参。

[008][租户模块]基于Caffeine的租户隔离与两级缓存实践

本项目代码:https://gitee.com/yunjiao-source/tutorials4j/tree/master/framework

在SaaS多租户系统中,缓存隔离是一个必须解决的问题——不同租户的数据不应相互干扰,同时又需要保证高性能。本文以一个轻量级的缓存路由框架为例,分析如何利用 Caffeine 实现租户级别的本地缓存隔离,并进一步扩展为 Caffeine + Redis 的两级缓存方案。

一、核心需求与设计目标

  • 租户隔离:每个租户拥有独立的缓存空间,避免数据窜读。
  • 动态路由:根据当前请求的租户ID,自动选择对应的缓存管理器。
  • 两级缓存:本地Caffeine(一级)提供纳秒级访问,远程Redis(二级)提供跨节点共享,兼顾性能与一致性。
  • 延迟初始化:仅在租户首次访问时创建其专属缓存管理器,避免启动时占用大量内存。

二、基础抽象:AbstractRoutingCacheManager

该抽象类模仿Spring的AbstractRoutingDataSource,作为所有路由缓存管理器的基类。

public abstract class AbstractRoutingCacheManager<T extends CacheManager> implements CacheManager {
   
    private Map<Object, T> targetCacheManagers = new ConcurrentHashMap<>();

    @Override
    public Cache getCache(String name) {
   
        return determineTargetDataSource().getCache(name);
    }

    protected CacheManager determineTargetDataSource() {
   
        Object lookupKey = determineCurrentLookupKey();
        return targetCacheManagers.computeIfAbsent(lookupKey, this::createCacheManager);
    }

    protected abstract Object determineCurrentLookupKey();
    protected abstract T createCacheManager(Object name);
}

设计要点

  • determineCurrentLookupKey():由子类实现,通常从TenantContextHolder中获取当前线程的租户ID。
  • computeIfAbsent:保证同一租户只创建一个缓存管理器实例,实现按需延迟加载。
  • 完全委托模式:所有CacheManager接口方法都转发给当前租户对应的管理器。

三、租户级Caffeine缓存管理器

public class TenantCaffeineCacheManager extends AbstractRoutingCacheManager<CaffeineCacheManager> {
   
    private final CaffeineCacheManagerCreator caffeineCacheManagerCreator;

    @Override
    protected Object determineCurrentLookupKey() {
   
        return TenantContextHolder.get();
    }

    @Override
    protected CaffeineCacheManager createCacheManager(Object name) {
   
        return caffeineCacheManagerCreator.newInstance();
    }
}

亮点

  • 每个租户获得一个独立的CaffeineCacheManager实例,底层Caffeine缓存完全隔离。
  • CaffeineCacheManagerCreator负责生产默认配置的Caffeine管理器(例如设置过期时间、最大条目等),支持后续定制。

四、两级缓存:Caffeine + Redis

多级缓存管理器MultiLevelCacheManager(未完整贴出,但从创建器可推断)组合了一级本地缓存和二级分布式缓存。其创建器如下:

public class TenantMultiLevelCacheManagerCreator implements Supplier<MultiLevelCacheManager> {
   
    private final TenantCaffeineCacheManagerCreator tenantCaffeineCacheManagerCreator;
    private final RedisCacheManagerCreator redisCacheManagerCreator;

    @Override
    public MultiLevelCacheManager get() {
   
        // 双重检查锁单例
        instance = new MultiLevelCacheManager(
            tenantCaffeineCacheManagerCreator.get(),
            redisCacheManagerCreator.get()
        );
        return instance;
    }
}

工作流程推测

  1. 读操作:先从Caffeine获取,未命中则查Redis,命中后回填Caffeine。
  2. 写操作:同时更新Redis并失效Caffeine(或更新Caffeine)。
  3. 租户维度的隔离由上下层共同保证:不同的租户使用不同的Caffeine实例,Redis中的key也会拼接租户ID前缀。

五、Spring自动配置与创建器模式

TenantCacheConfiguration提供@ConditionalOnMissingBean的Bean定义,使得使用者可以开箱即用或覆盖默认实现。

@Configuration(proxyBeanMethods = false)
public class TenantCacheConfiguration {
   
    @Bean
    @ConditionalOnMissingBean
    TenantCaffeineCacheManagerCreator tenantCaffeineCacheManagerCreator(CaffeineCacheManagerCreator caffeineCacheManagerCreator) {
   
        return new TenantCaffeineCacheManagerCreator(caffeineCacheManagerCreator);
    }

    @Bean
    @ConditionalOnMissingBean
    TenantMultiLevelCacheManagerCreator tenantMultiLevelCacheManagerCreator(
            TenantCaffeineCacheManagerCreator tenantCaffeineCacheManagerCreator,
            RedisCacheManagerCreator redisCacheManagerCreator) {
   
        return new TenantMultiLevelCacheManagerCreator(tenantCaffeineCacheManagerCreator, redisCacheManagerCreator);
    }
}

创建器模式的价值

  • 延迟单例:TenantCaffeineCacheManagerCreator保证全局只有一个路由缓存管理器实例,但内部会按需创建租户子管理器。
  • 解决循环依赖:通过Supplier接口避免Bean过早初始化,特别适合多级缓存组合场景。

六、使用场景与优势

场景 推荐方案 收益
单租户本地缓存 TenantCaffeineCacheManager 租户隔离,无脏数据
多租户共享数据(如配置) 普通CacheManager 无需隔离
高并发读 + 分布式一致性 两级缓存(Caffeine+Redis) 热数据本地命中,冷数据走Redis
租户动态创建/销毁 路由机制 + 租户上下文 无需重启,自动创建新租户管理器

性能考量

  • Caffeine是进程内缓存,同一租户的请求可以共享热点数据,大幅降低Redis压力。
  • 租户数量较多时,每个租户独立的管理器可能会产生一定内存开销(每个管理器有独立配置和缓存实例)。可通过maximumSize限制每租户缓存条目数。

七、扩展方向

  1. 动态缓存配置:不同租户可以有不同的Caffeine参数(如过期时间、最大容量)。改造createCacheManager方法,根据租户ID从配置中心获取差异化参数。
  2. 懒加载与淘汰:可在租户无活动一段时间后,移除其缓存管理器释放内存。
  3. 监控与统计:暴露每个租户的缓存命中率、大小等指标,便于运维。
  4. 多级缓存一致性:完善写操作时的广播失效机制(例如使用Redis Pub/Sub通知其他节点清理本地Caffeine)。

八、总结

这套基于AbstractRoutingCacheManager的设计,将“路由”与“具体缓存实现”解耦,实现了优雅的租户隔离。结合Caffeine的高性能和Redis的共享能力,构建了两级缓存方案,非常适合SaaS平台、多租户中间件等场景。开发者只需在请求入口设置TenantContextHolder,后续所有缓存操作即可自动路由到正确租户的命名空间,既保证了数据安全,又获得了极致性能。


附:代码文件结构

  • AbstractRoutingCacheManager.java:核心路由基类
  • TenantCaffeineCacheManager.java:租户Caffeine实现
  • TenantMultiLevelCacheManagerCreator.java:两级缓存创建器
  • TenantCacheConfiguration.java:Spring自动配置

通过这个示例,你可以轻松扩展支持Ehcache、Hazelcast等其他缓存提供者,并实现相同的租户隔离能力。

目录
相关文章
|
3月前
|
缓存 安全 搜索推荐
[004][缓存模块]Caffeine缓存自定义:构建灵活的Spring Boot缓存管理器
本文介绍Spring Boot中Caffeine缓存的灵活定制方案:通过自定义`FlexibleCaffeineCacheManager`,支持按缓存名(如users/products)独立配置过期策略、容量等参数,兼顾全局默认与个性化需求;结合线程安全创建器、属性合并机制及无缝Spring集成,实现高性能、易扩展、零侵入的本地缓存管理。(239字)
187 2
|
3月前
|
缓存 NoSQL Java
[006][缓存模块] 两级缓存实战:基于 Caffeine + Redis 的多级缓存设计与实现
本文介绍基于Caffeine(本地)+ Redis(分布式)的两级缓存实战方案,通过自定义`MultiLevelCache`与`MultiLevelCacheManager`,实现Spring Cache标准接口下的透明多级缓存:读优先本地(纳秒级)、未命中查Redis并回填;写同步更新两级,兼顾高性能与数据共享。代码开源可直接集成。
297 0
|
3月前
|
人工智能 运维 架构师
我在 AIP 智能体平台踩过的坑,都在这篇企业 AI 落地经验里了
软件架构师罗小东分享企业AI落地实战经验:聚焦AIP智能体平台建设中的真实坑点与解法——涵盖智能体全生命周期管理、多源知识库语义检索、MCP工具集成及多模型中立架构设计,强调“解决问题”而非堆砌功能。(239字)
|
3月前
|
缓存 NoSQL Java
[012][缓存模块]基于 Spring Cache 的缓存操作模版,支持Caffeine缓存, Redis缓存及两级缓存
本项目基于Spring Cache抽象,提供Caffeine本地缓存、Redis分布式缓存及两级缓存(Caffeine+Redis)的统一操作模板。通过工厂模式与模板方法,封装get/put/delete/异步加载等能力,支持租户隔离、类型安全与Spring生命周期集成,显著简化缓存接入。(239字)
150 3
[012][缓存模块]基于 Spring Cache 的缓存操作模版,支持Caffeine缓存, Redis缓存及两级缓存
|
3月前
|
算法 Java 数据库连接
[011][数据模块]基于雪花算法的 Hibernate 分布式主键生成器设计与实现
本文介绍基于雪花算法的Hibernate分布式主键生成器,支持`@SnowflakeIDGenerator`注解一键集成,自动适配String/Long类型主键,具备全局唯一、趋势递增、高性能等优势,适用于分库分表与微服务场景。(239字)
248 4
|
3月前
|
存储 安全 Java
[007][租户模块]基于 TransmittableThreadLocal 与 TaskDecorator 的租户上下文传递设计
本文介绍基于TransmittableThreadLocal与TaskDecorator的SaaS租户上下文传递方案:Web层通过拦截器注入租户,异步任务借助装饰器自动传递,确保线程池中租户标识不丢失。设计轻量、非侵入、可扩展,兼顾默认租户容错与资源清理,适用于多租户Spring Boot应用。(239字)
183 2
|
3月前
|
缓存 druid Java
[010][数据模块]多数据源管理器在 Hibernate 多租户中的应用
本文介绍基于`DataSourceRoutingManager`的Hibernate数据库级多租户方案:通过动态路由与连接池模板复制(支持HikariCP/DBCP2/Druid),实现租户专属数据源的懒加载、缓存与无缝集成,配置简洁、扩展性强,已落地多个SaaS生产项目。(239字)
151 2
|
3月前
|
数据采集 Linux API
阿里云部署 Hermes Agent/OpenClaw 保姆级图文流程:免费API配置与新手常见问题解答
OpenClaw(Clawdbot)作为一款具备长期记忆与个性化能力的AI助手,凭借爬虫、数据分析、自动化操作等功能成为高效的工作辅助工具,但其本地部署过程中存在的环境配置、网络限制、模型对接等问题,让零基础新手望而却步。2026年针对这一问题,本文整理了阿里云、MacOS、Linux、Windows11多平台的OpenClaw本地部署完整步骤,同时包含阿里云百炼免费大模型API配置方法,并对部署和使用中的常见问题逐一解答,让新手也能零门槛实现OpenClaw的本地搭建与功能使用。
471 0
|
3月前
|
人工智能 关系型数据库 MySQL
【第6天】每天一个MySQL知识点,百日打怪升级
本文为DBA老兵总结的索引优化实战指南:聚焦“何时建、何时不建”核心问题。详解索引选择性(唯一值/总行数)、失效场景(低区分度、函数运算、隐式转换)及建索引黄金法则——WHERE/JOIN/ORDER BY/GROUP BY高频字段优先,状态类、低选择性列坚决不建。附EXPLAIN实战分析与AI辅助诊断技巧。(239字)
255 1
|
3月前
|
人工智能 搜索推荐 SEO
生成式搜索时代,如何评估品牌在 AI 答案中的可见性?
本文介绍生成式引擎优化(GEO)——AI问答时代品牌可见性评估新方法。聚焦ChatGPT、通义等大模型答案页,通过问题集设计、答案采样、品牌提及分析、语义表达评估与结果复盘,系统衡量品牌在AI回答中是否被提及、理解准确、归类正确、描述正向及表现稳定,助力企业科学布局AIGC时代品牌建设。(239字)