在高并发业务系统中,数据访问性能直接影响用户体验,单一缓存层很难同时兼顾低延迟、高吞吐和高可用。通过Redis读写分离搭配本地二级缓存构建多级加速层,既能利用本地缓存的极低访问耗时,又能借助Redis的分布式能力保障数据一致性,还能通过读写分离提升整体读性能。

多级缓存架构整体设计
整个加速层分为三层,从近到远依次是本地二级缓存层、Redis读节点层、Redis写节点层,底层对接数据库。请求优先访问本地缓存,未命中则访问Redis读节点,仍未命中则查询数据库并回写缓存。写操作统一走Redis写节点,再同步到读节点,同时触发本地缓存失效。
各层职责划分
- 本地二级缓存:使用Caffeine等本地缓存组件,存储高频访问的热点数据,访问耗时在微秒级,减少跨网络请求
- Redis读写分离集群:写节点负责处理所有写请求和缓存更新,读节点负责处理读请求,通过主从同步保证数据最终一致
- 数据库层:作为数据最终存储源,仅在缓存全部未命中时访问,避免数据库压力过大
核心实现逻辑
读流程实现
读请求的处理流程遵循就近访问原则,具体步骤如下:
- 首先查询本地二级缓存,若命中则直接返回数据
- 本地缓存未命中时,查询Redis读节点,若命中则返回数据,并回写本地缓存
- Redis读节点未命中时,查询数据库获取最新数据,先回写Redis读节点,再回写本地缓存,最后返回数据
以下是Java语言实现的读流程核心代码示例:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class MultiLevelCacheService {
// 本地二级缓存,设置最大容量1000,过期时间5分钟
private Cache<String, String> localCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// Redis读节点连接
private Jedis redisReadClient = new Jedis("192.168.0.2", 6379);
// 数据库查询模拟
private String queryFromDb(String key) {
// 实际业务中对接数据库查询逻辑
return "db_value_" + key;
}
public String get(String key) {
// 第一步:查本地缓存
String value = localCache.getIfPresent(key);
if (value != null) {
return value;
}
// 第二步:查Redis读节点
value = redisReadClient.get(key);
if (value != null) {
// 回写本地缓存
localCache.put(key, value);
return value;
}
// 第三步:查数据库
value = queryFromDb(key);
// 回写Redis
redisReadClient.setex(key, 300, value);
// 回写本地缓存
localCache.put(key, value);
return value;
}
}
写流程实现
写操作需要保证数据一致性,流程如下:
- 所有写请求发送到Redis写节点,更新缓存数据
- Redis写节点通过主从同步机制,将数据同步到所有读节点
- 写操作完成后,发送缓存失效消息到所有应用节点,清除对应的本地缓存
写流程核心代码示例如下:
import redis.clients.jedis.Jedis;
import java.util.List;
public class CacheWriteService {
// Redis写节点连接
private Jedis redisWriteClient = new Jedis("192.168.0.1", 6379);
// 模拟本地缓存引用,实际业务中可通过消息队列通知所有节点
private List<MultiLevelCacheService> allCacheServices;
public void set(String key, String value) {
// 更新Redis写节点
redisWriteClient.setex(key, 300, value);
// 触发本地缓存失效,实际场景可使用Redis Pub/Sub或者消息队列通知
for (MultiLevelCacheService service : allCacheServices) {
service.invalidateLocalCache(key);
}
}
}
数据一致性保障策略
多级缓存面临的最大问题是数据一致性,需要采用以下策略保障:
- 写时失效策略:写操作完成后立即清除本地缓存,避免本地缓存存储旧数据
- Redis过期时间兜底:所有Redis缓存设置合理的过期时间,即使同步出现问题,过期后也会自动从数据库加载最新数据
- 本地缓存短过期:本地缓存的过期时间设置短于Redis缓存,减少数据不一致的时间窗口
- 读写分离同步监控:监控Redis主从同步延迟,延迟过高时临时将读请求路由到写节点,避免读取到旧数据
高可用与故障降级
为了保障架构的高可用,需要设计完善的故障降级策略:
Redis读节点故障
当Redis读节点不可用时,读请求自动降级到Redis写节点,同时触发告警,待读节点恢复后切回正常流程。可以在代码中添加重试和熔断逻辑:
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class SafeRedisReadClient {
private Jedis readClient;
private Jedis writeClient;
private boolean readAvailable = true;
private long lastCheckTime = 0;
public String get(String key) {
// 每30秒检查一次读节点可用性
if (System.currentTimeMillis() - lastCheckTime > 30000) {
try {
readClient.ping();
readAvailable = true;
} catch (Exception e) {
readAvailable = false;
}
lastCheckTime = System.currentTimeMillis();
}
if (readAvailable) {
try {
return readClient.get(key);
} catch (Exception e) {
readAvailable = false;
// 降级到写节点
return writeClient.get(key);
}
} else {
return writeClient.get(key);
}
}
}
Redis全量故障
当Redis集群完全不可用时,读请求直接走本地缓存,本地缓存未命中则查询数据库,写操作暂时写入数据库,待Redis恢复后再同步缓存数据,避免系统完全不可用。
本地缓存故障
如果本地缓存组件出现异常,直接跳过本地缓存层,所有读请求访问Redis,不影响整体功能。
架构适用场景与注意事项
该架构适合读多写少、对访问延迟要求高的业务场景,比如商品详情页、首页推荐、配置信息查询等。使用时需要注意:
- 本地缓存只存储热点数据,避免占用过多应用内存
- 缓存key需要设计合理的命名规范,避免key冲突
- 写操作尽量批量处理,减少缓存失效的通知次数
- 定期监控各层缓存的命中率,根据命中率调整缓存策略和容量