电商购物车场景的Redis集群架构与一致性权衡
去年大促期间,支撑用户加购、购物车列表查询的Redis单机实例在QPS达到8.7万时出现响应延迟陡增,且主节点宕机后30秒的哨兵选举窗口内,加购接口失败率升至12%,直接影响大促转化率,由此触发购物车场景的Redis架构演进与缓存一致性重构。
单机瓶颈与高可用架构选型
单机Redis理论QPS上限约10万,生产环境需预留30%余量应对突发流量,主从+哨兵方案的故障切换时间通常在20-40秒,无法满足购物车场景99.99%的可用性要求。选型阶段核心权衡点是代理层复杂度、故障切换速度、社区活跃度,对比Codis与Redis Cluster两个主流方案:Codis基于代理层实现分片,需改造客户端兼容代理协议,且社区维护节奏放缓,对Redis新特性支持滞后;Redis Cluster是官方原生无中心方案,支持槽位自动迁移、节点故障自动转移,无需额外代理层,更适合千万级用户、日均千万次加购请求的场景。最终采用3主3从的Cluster部署,单分片预估承载4万QPS,预留了水平扩展的槽位空间。
缓存一致性设计与异常兜底
购物车场景读请求占比70%(用户频繁刷新列表),写请求包含加购、修改数量、删除商品,对一致性要求高于普通商品详情缓存,但允许分钟级的短暂不一致。放弃原生Cache Aside模式,改用「写DB+异步删缓存+兜底全量刷新」的方案:写请求先落库,再通过RocketMQ异步删除用户购物车缓存,消息配置重试机制与死信队列,避免删缓存失败导致脏数据;每日低峰期触发全量购物车缓存预热,修复异步删除的遗漏问题。针对热点商品购物车的缓存击穿问题,采用逻辑过期方案:缓存数据携带过期时间戳,后台异步线程提前更新过期缓存,避免大量请求直接打到DB。同时开启Lettuce客户端的集群自适应路由,自动处理槽位迁移、ASK重定向场景,避免迁移期间的请求失败。
生产落地的关键权衡与配置
架构选型需在可用性、性能、复杂度之间做取舍:为降低大促期间主线程阻塞风险,关闭了Redis的RDB与AOF持久化,依靠MySQL做最终数据持久化,压测显示该调整可降低15%的写延迟,且购物车场景可容忍最近5分钟的加购数据丢失。主从复制配置repl-backlog-size为1GB,避免大促突发流量导致主从断开后全量同步的带宽压力。慢查询阈值设置为1ms,对超过阈值的请求强制打印堆栈,排查出热点Key的BigKey问题后,将大促爆品的购物车数据拆分为用户维度+商品维度的两个Key,降低单Key的大小。
核心配置与业务逻辑实现如下:
# 购物车服务Redis Cluster核心配置
spring:
redis:
cluster:
nodes: 10.0.12.31:6379,10.0.12.32:6379,10.0.12.33:6379
max-redirects: 3
lettuce:
pool:
max-active: 300
max-wait: 50ms
@Transactional
public void addCartItem(Long userId, CartItem item) {
// 先写DB保证数据持久化
cartMapper.insert(userId, item);
// 异步发送删缓存消息,避免阻塞主流程
rocketMQTemplate.asyncSend("cart-cache-delete-topic",
userId.toString(), new SendCallback() {
@Override
public void onSuccess(SendResult result) {}
@Override
public void onException(Throwable e) {
log.error("删缓存消息发送失败,userId:{}", userId, e);
}
});
}
架构落地后,大促期间购物车服务Redis集群承载了14.2万峰值QPS,平均响应延迟1.1ms,主节点故障时自动切换时间稳定在8秒以内,缓存不一致问题从日均47例降至几乎为0,且后续流量增长时可通过新增分片平滑扩容。
- 点赞
- 收藏
- 关注作者
评论(0)