基于 XXL-CACHE 的 Spring Boot 多级缓存(L1 本地 + L2 分布式)自动配置组件,一行配置即可获得高命中率 + 低延迟 + 强一致性的多级缓存能力。
cache-mult 组件基于 XXL-CACHE 实现多级缓存架构,将本地缓存(L1)与分布式缓存(L2)透明组合:
两级缓存协同工作:读请求优先命中本地 L1,未命中则穿透到 L2,再未命中才查询数据源。写操作会同时更新/失效两级缓存,通过 Redis Pub/Sub 通知其他节点同步失效 L1。
dependencies {
implementation("com.mcst:cache-mult")
}<dependency>
<groupId>com.mcst</groupId>
<artifactId>cache-mult</artifactId>
</dependency>com.mcst:easyfk-dependencies)统一管理,无需手动指定版本号。在 application.yml 中配置:
xxl:
cache:
l1:
provider: caffeine
maxSize: 10000
expireAfterWrite: 600
l2:
provider: redisson
serializer: java
nodes: redis://127.0.0.1:6379
password: your-passwordimport com.xxl.cache.core.XxlCacheHelper;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public User getUserById(Long userId) {
String cacheKey = "user:" + userId;
User user = XxlCacheHelper.get(cacheKey);
if (user != null) {
return user;
}
user = queryFromDB(userId);
XxlCacheHelper.set(cacheKey, user);
return user;
}
}| 配置项 | 默认值 | 说明 |
|---|---|---|
| xxl.cache.l1.provider | caffeine | L1 缓存提供者 |
| xxl.cache.l1.maxSize | 10000 | L1 最大缓存条目数 |
| xxl.cache.l1.expireAfterWrite | 600 | L1 写入后过期时间(秒) |
| xxl.cache.l2.provider | redisson | L2 缓存提供者 |
| xxl.cache.l2.serializer | java | L2 序列化方式 |
| xxl.cache.l2.nodes | (空) | Redis 节点地址 |
| xxl.cache.l2.user | (空) | Redis 用户名(ACL 认证时使用) |
| xxl.cache.l2.password | (空) | Redis 密码 |
L1 基于 Caffeine 实现进程内高速缓存:
控制本地缓存可存储的最大条目数,超出后按 LRU(最近最少使用)策略淘汰。
xxl:
cache:
l1:
maxSize: 10000选型建议:
1000 ~ 500010000(默认)50000 ~ 100000控制 L1 缓存条目的存活时间(秒),到期后自动失效。这是 L1 与 L2 数据一致性的保底机制。
xxl:
cache:
l1:
expireAfterWrite: 600选型建议:
60 ~ 300(1~5 分钟)600(默认 10 分钟)3600 ~ 86400(1 小时~1 天)支持单机和集群模式:
# 单机模式
xxl:
cache:
l2:
nodes: redis://127.0.0.1:6379
# 集群模式(逗号分隔多个节点)
xxl:
cache:
l2:
nodes: redis://node1:6379,redis://node2:6379,redis://node3:6379
# 带 SSL
xxl:
cache:
l2:
nodes: rediss://127.0.0.1:6379| 值 | 说明 | 适用场景 |
|---|---|---|
| java | Java 原生序列化 | 默认选项,兼容性好 |
| json | JSON 序列化 | 可读性好,跨语言场景 |
| kryo | Kryo 序列化 | 高性能,体积小 |
┌─────────────────────────────────────────────────────────────┐
│ 应用节点 A │
│ ┌─────────┐ 未命中 ┌─────────┐ 未命中 ┌────────┐ │
│ │ L1 本地 │ ──────────→ │ L2 Redis │ ──────────→ │ 数据源 │ │
│ │ Caffeine │ ←────────── │ 分布式 │ ←────────── │ DB/API │ │
│ └─────────┘ 回填 L1 └─────────┘ 回填 L2 └────────┘ │
└────────┬────────────────────────┬───────────────────────────┘
│ │
│ Redis Pub/Sub 失效通知 │
│ │
┌────────▼────────────────────────▼───────────────────────────┐
│ 应用节点 B │
│ ┌─────────┐ ┌─────────┐ ┌────────┐ │
│ │ L1 本地 │ │ L2 Redis │ │ 数据源 │ │
│ │ Caffeine │ │ 共享同一 │ │ │ │
│ └─────────┘ │ Redis │ └────────┘ │
└─────────────────────────────────────────────────────────────┘请求 → 查 L1(本地 Caffeine)
├── 命中 → 直接返回(纳秒级)
└── 未命中 → 查 L2(Redis)
├── 命中 → 回填 L1 → 返回(毫秒级)
└── 未命中 → 查数据源 → 回填 L2 + L1 → 返回cache-mult 通过以下机制保证多节点间 L1 数据的一致性:
expireAfterWrite 作为兜底机制,即使 Pub/Sub 消息丢失,过期后也会从 L2 重新加载cache-mult 兼容 Spring Cache 注解体系,可直接使用标准注解:
@Service
public class ProductService {
@Cacheable(value = "products", key = "#productId")
public Product getProduct(Long productId) {
return productRepository.findById(productId).orElse(null);
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
@CacheEvict(value = "products", key = "#productId")
public void deleteProduct(Long productId) {
productRepository.deleteById(productId);
}
@CacheEvict(value = "products", allEntries = true)
public void clearAllProductCache() {
// 清空 products 缓存空间所有条目
}
}通过 XxlCacheHelper 直接操作多级缓存:
import com.xxl.cache.core.XxlCacheHelper;
XxlCacheHelper.set("user:10001", userObject);
User user = XxlCacheHelper.get("user:10001");
XxlCacheHelper.remove("user:10001");@Service
public class UserService {
@Cacheable(value = "users", key = "#userId")
public UserDTO getUserById(Long userId) {
return userMapper.selectById(userId);
}
@CacheEvict(value = "users", key = "#userId")
public void updateUser(Long userId, UserDTO userDTO) {
userMapper.updateById(userDTO);
}
}配置:
xxl:
cache:
l1:
maxSize: 5000
expireAfterWrite: 300@Service
public class DictService {
@Cacheable(value = "dict", key = "#dictType + ':' + #dictCode")
public String getDictLabel(String dictType, String dictCode) {
return dictMapper.selectLabel(dictType, dictCode);
}
}配置:
xxl:
cache:
l1:
maxSize: 50000
expireAfterWrite: 3600@Service
public class ProductService {
@Cacheable(value = "product-detail", key = "#productId")
public ProductDetailVO getProductDetail(Long productId) {
return assembleProductDetail(productId);
}
}配置:
xxl:
cache:
l1:
maxSize: 100000
expireAfterWrite: 120存入多级缓存的对象必须实现 Serializable 接口(使用 Java 序列化时),否则 L2 写入会失败:
// 正确:实现 Serializable
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
}
// 错误:未实现 Serializable,L2 写入异常
public class UserDTO {
private Long id;
private String name;
}cache-mult 默认不缓存 null 值。高并发下如果大量请求查询不存在的数据,会反复穿透到数据源。建议在业务层处理:
@Cacheable(value = "users", key = "#userId", unless = "#result == null")
public UserDTO getUserById(Long userId) {
UserDTO user = userMapper.selectById(userId);
return user != null ? user : UserDTO.EMPTY;
}大量缓存同时过期会导致瞬间压力全部打到数据源。建议:
| 组件 | 定位 | 与 cache-mult 的关系 |
|---|---|---|
| cache-caffeine | 纯本地缓存 | cache-mult 的 L1 层使用 Caffeine |
| cache-redis | 纯 Redis 缓存 | cache-mult 的 L2 层使用 Redis |
| cache-mult | 多级缓存 | 自动组合 L1 + L2,无需单独引入上述两个 |
cache-mult 自带 Caffeine 和 Redisson 依赖,不需要额外引入 cache-caffeine 或 cache-redis。同时使用可能导致 Bean 冲突。maxSize 过小 → L1 命中率低 → 频繁穿透到 L2 → 网络开销增大
maxSize 过大 → 本地内存占用高 → 可能触发 GC → 影响应用稳定性推荐 maxSize = 热点数据总量 × 1.2(留 20% 余量)
内存占用估算 = maxSize × 平均对象大小| 数据特征 | 推荐 expireAfterWrite | 命中率 | 一致性 |
|---|---|---|---|
| 几乎不变 | 1800 ~ 3600 秒 | 极高 | 较弱 |
| 偶尔变更 | 300 ~ 600 秒 | 高 | 较好 |
| 频繁变更 | 30 ~ 120 秒 | 中等 | 强 |
| 实时性要求极高 | 不建议使用 L1 | — | — |
allkeys-lru)| 序列化方式 | 速度 | 体积 | 可读性 | 推荐场景 |
|---|---|---|---|---|
| java | 中等 | 较大 | 不可读 | 默认选项,兼容性好 |
| json | 中等 | 中等 | 可读 | 调试友好,跨语言 |
| kryo | 快 | 小 | 不可读 | 追求极致性能 |
短暂的不一致是正常的(最终一致性模型)。如果发现长时间不一致:
expireAfterWrite 是否合理L1 是独立的本地缓存,Redis 宕机后 L1 中已有的数据仍然可用。但新的缓存未命中无法穿透到 L2,会直接访问数据源。Redis 恢复后多级缓存自动恢复正常。
建议在 key 中加入服务名前缀以避免冲突,例如 order-service:user:10001。
兼容。cache-mult 底层集成了 XXL-CACHE,支持 Spring Cache 标准注解(@Cacheable、@CachePut、@CacheEvict)。
| 场景 | 推荐方案 |
|---|---|
| 单机应用,数据量小 | cache-caffeine(纯本地缓存) |
| 分布式应用,一致性要求高 | cache-redis(纯 Redis 缓存) |
| 分布式应用,高并发读 + 兼顾一致性 | cache-mult(多级缓存) |
cache-mult — L1 本地 + L2 分布式,多级缓存加速数据访问。