热点数据过期时间设计:平衡一致性与性能
问题的本质
过期时间(TTL)是缓存系统最核心的参数之一。设得太短,缓存命中率低,数据库压力大;设得太长,数据可能过时,用户看到脏数据。对于热点数据,这个问题更加突出——击穿风险、数据一致性、存储成本交织在一起。
过期时间设计的核心矛盾
| 设定方向 | 优势 | 劣势 |
|---|---|---|
| TTL 过长 | 高命中率、低 DB 压力 | 数据可能不一致、占用内存 |
| TTL 过短 | 数据新鲜、节省内存 | 低命中率、高 DB 压力、雪崩风险 |
设计策略
1. 根据数据变化频率设计
不同类型的热点数据有不同的变化频率,应设置不同的 TTL:
// 几乎不变的数据:配置信息、词典
CONFIG_TTL = 24 * 60 * 60; // 24小时
// 低频变化:用户基本信息、商品描述
USER_BASIC_TTL = 60 * 60; // 1小时
// 中频变化:商品销量、库存
PRODUCT_STATS_TTL = 5 * 60; // 5分钟
// 高频变化:实时热度、排行榜
REALTIME_TTL = 10; // 10秒
2. 随机化过期时间
同一类热点数据的 TTL 加上随机偏移,避免大面积同时过期:
// 基础 TTL 30分钟,随机偏移 ±5 分钟
int baseTtl = 1800;
int jitter = new Random().nextInt(600);
int ttl = baseTtl + jitter;
redis.set(key, value, ttl, TimeUnit.SECONDS);
// 或者更均匀的分布:±30%
int ttl = (int)(baseTtl * (0.7 + Math.random() * 0.6));
效果:原 TTL 30 分钟,1000 个 key 会在同一秒过期。随机化后,过期时间均匀分布在 21~39 分钟内,大大降低同时穿透的概率。
3. 分层 TTL 设计
将数据按照热度分层,不同层级使用不同的 TTL:
一级缓存(本地内存 Caffeine):TTL = 5 秒,存储最热的 1000 条
二级缓存(Redis):TTL = 30 分钟,存储全量热点
三级存储(MySQL):作为最终数据源
越靠近用户的层 TTL 越短,保证数据新鲜;越靠近存储的层 TTL 越长,保护数据库。
4. 动态 TTL
根据实际访问频率动态调整 TTL:
public class DynamicTtl {
private final Map<String, AtomicInteger> accessCount = new ConcurrentHashMap<>();
public void recordAccess(String key) {
accessCount.computeIfAbsent(key, k -> new AtomicInteger()).incrementAndGet();
}
public int calculateTtl(String key) {
int count = accessCount.getOrDefault(key, new AtomicInteger()).get();
if (count > 1000) return 3600; // 超热点 1小时
if (count > 100) return 600; // 热点 10分钟
return 60; // 普通 1分钟
}
}
5. 逻辑过期(软过期)
设置一个比实际 TTL 更短的逻辑过期时间,过期后返回旧数据并触发异步刷新:
public class CacheItem<T> {
private T data;
private long createTime;
// 物理 TTL 设为 1 小时,逻辑 TTL 设为 50 分钟
public boolean isLogicallyExpired() {
return System.currentTimeMillis() - createTime > 50 * 60 * 1000;
}
}
public T getSmart(String key) {
CacheItem<T> item = redis.get(key);
if (item == null) return loadFresh(key); // 穿透了
if (item.isLogicallyExpired()) {
threadPool.submit(() -> loadFresh(key)); // 异步刷新
}
return item.data; // 先返回旧数据
}
不同业务场景的最佳实践
| 业务场景 | TTL 策略 | 理由 |
|---|---|---|
| 商品详情 | 10~30 分钟 + 随机偏移 | 时效性要求不高,命中率优先 |
| 库存数量 | 5~10 秒 | 实时性要求高,使用短 TTL |
| 用户 Session | 30 分钟 + 续期 | 每次访问重新刷新 TTL |
| 配置字典 | 1~24 小时 | 几乎不变,可手动刷新 |
| 排行榜 | 30 秒 ~ 1 分钟 | 不需要实时精准,允许短暂延迟 |
面试要点
- 过期时间设计没有通用答案,取决于数据一致性要求和系统负载
- 随机化过期时间是性价比最高的防雪崩手段
- 动态 TTL 和逻辑过期是高阶方案,体现对问题的深入理解
- 能结合多级缓存设计分层 TTL 是加分项
- 面试时可以给出的黄金建议:“先做随机化 TTL,再配合逻辑过期,最后考虑动态调整”
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容