1. 缓存与数据库同时故障的系统韧性设计
当缓存(Redis)与数据库(MySQL)同时发生故障时,如何设计系统以保证整体韧性,避免雪崩与全站不可用?
- 缓存与数据库故障的确定性降级策略
- 多级缓存与兜底数据的设计
- 熔断、限流与本地缓存兜底的组合
缓存与数据库同时故障时,系统应具备"逐级降级、最终兜底"的能力。设计上分为三层:第一层是本地缓存(如 Caffeine)作为进程内兜底,即使 Redis 不可用也能返回最近一次的数据;第二层是 Redis 缓存,正常时承担主要读流量;第三层是数据库。当 Redis 与数据库同时故障时,唯一可依赖的是本地缓存中的历史数据与静态兜底数据(如默认配置、预设的降级响应)。同时需要配合限流与熔断,将请求快速失败(fail fast)而不是排队等待,避免线程与连接池被耗尽。对于非核心数据(如推荐、统计),可直接返回缓存中的过期数据或预设默认值;对于核心业务(如支付、转账),则必须拒绝降级并快速失败,保证数据安全。关键是用"降级开关"(如配置中心动态开关)控制各级降级策略的启停,并让监控告警第一时间感知故障状态。
韧性设计的核心是"接受故障是常态",通过层次化的降级与兜底,把故障影响面压缩到最小。本地缓存兜底是无状态系统最后一道防线,即便外部依赖全部不可用,也能维持基本可用性。同时快速失败(fail fast)比无限等待更符合韧性目标,因为阻塞等待会放大资源消耗并引发级联故障。
// 降级开关:缓存+数据库均故障时返回本地兜底数据
public String getProductName(long productId) {
String name = localCache.getIfPresent(productId); // 进程内缓存
if (name != null) return name;
try {
name = redis.get("product:" + productId); // Redis 缓存
if (name != null) { localCache.put(productId, name); return name; }
name = productDao.queryById(productId); // 数据库
redis.set("product:" + productId, name);
localCache.put(productId, name);
return name;
} catch (Exception e) {
// 全部故障:返回兜底静态数据
return FALLBACK_NAME;
}
}