示例:
public Product getProduct(String id) {
Product product = cache.get(id);
if (product == null) {
product = db.query("SELECT * FROM product WHERE id = ?", id);
cache.set(id, product, 300); // 缓存5分钟
}
return product;
}
hot:item:123
→ hot:item:123_{shardId}
)。场景 | 问题描述 | 解决方案 |
---|---|---|
先更新数据库,后删缓存 | 删除缓存失败,后续请求读到旧数据。 | 延迟双删:更新数据库 → 删除缓存 → 延迟几百毫秒再删一次。 |
先删缓存,后更新数据库 | 删缓存后、更新数据库前,其他请求可能读到旧值并回填缓存。 | 异步重试:删除缓存后,通过消息队列确保数据库更新成功,否则重试删除操作。 |
并发写导致覆盖 | 多个线程同时更新同一数据,缓存与数据库不一致。 | 分布式锁:更新时加锁(如Redis SETNX),串行化操作。 |
示例:延迟双删伪代码
public void updateProduct(Product product) {
// 1. 更新数据库
db.update(product);
// 2. 删除缓存
cache.delete(product.getId());
// 3. 延迟再次删除(应对并发场景)
executor.schedule(() -> cache.delete(product.getId()), 500, TimeUnit.MILLISECONDS);
}
NULL
并设置短TTL(如30秒)。示例:
public Product getProduct(String id) {
Product product = cache.get(id);
if (product != null) {
return product;
}
if (bloomFilter.mightContain(id)) { // 布隆过滤器检查
product = db.query("SELECT * FROM product WHERE id = ?", id);
if (product != null) {
cache.set(id, product, 300);
} else {
cache.set(id, NULL, 30); // 缓存空值
}
}
return product;
}
TTL + random(0, 300)
)。示例:互斥锁实现
public Product getProduct(String id) {
Product product = cache.get(id);
if (product == null) {
Lock lock = redisLock.lock(id); // 获取分布式锁
try {
product = cache.get(id); // 双重检查
if (product == null) {
product = db.query("SELECT * FROM product WHERE id = ?", id);
cache.set(id, product, 300);
}
} finally {
redisLock.unlock(id);
}
}
return product;
}
product:123
→ product:123_{shardId}
)。策略 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
Cache-Aside | 通用场景,需强一致性控制 | 灵活,缓存与数据库解耦 | 需处理一致性问题 |
Read-Through | 缓存作为主要数据源 | 简化代码,自动加载数据 | 缓存层需支持加载逻辑 |
Write-Through | 写操作频繁且需强一致性 | 数据更新实时同步 | 写延迟较高 |
Write-Behind | 高吞吐写场景(如日志记录) | 写性能高,批量更新数据库 | 数据可能丢失(未持久化前宕机) |