当前位置: 首页 > article >正文

Redis最佳实践——购物车优化详解

在这里插入图片描述

Redis在电商购物车高并发读写场景下的优化实践


一、购物车业务场景分析
  1. 典型操作特征

    • 读/写比例 ≈ 8:2
    • 高峰QPS可达10万+
    • 单用户最大商品数500+
    • 操作类型:增删改查、全选/反选、数量修改
  2. 技术挑战

    • 高并发下的数据一致性
    • 海量数据存储与快速访问
    • 实时价格计算与库存校验
    • 分布式环境下的会话管理

二、核心数据结构设计优化

1. 存储结构方案对比

方案优点缺点
String+JSON简单直观修改需反序列化整个数据
Hash结构支持字段级操作嵌套结构处理略复杂
Sorted Set天然支持排序存储成本较高
混合结构平衡性能与灵活性实现复杂度略高

2. 最终数据结构设计

// Key设计:cart:{userType}:{userId}
String cartKey = "cart:user:10001"; // Value结构:
// Hash结构存储商品基础信息
Map<String, String> itemData = new HashMap<>();
itemData.put("sku:1001", "{\"quantity\":2,\"selected\":1,\"price\":5999,\"timestamp\":1717025661}");// Sorted Set维护操作顺序
jedis.zadd(cartKey + ":zset", System.currentTimeMillis(), "sku:1001");

三、读写分离架构设计

1. 多级缓存架构

首次访问
缓存穿透
缓存未命中
缓存命中
缓存命中
回写
回写
客户端
读请求
本地缓存
Redis集群
数据库
返回数据

2. 各层缓存配置

缓存层级技术选型容量过期策略
本地缓存Caffeine10万用户基于大小+访问时间(1分钟)
Redis缓存Hash+Zset1TB内存动态TTL+LRU淘汰
持久化存储MySQL+TiDB无限扩展事务保障

四、高并发写入优化

1. 批量操作管道化

public void batchAddItems(String userId, List<CartItem> items) {try (Jedis jedis = jedisPool.getResource()) {Pipeline pipeline = jedis.pipelined();String cartKey = buildCartKey(userId);items.forEach(item -> {String field = "sku:" + item.getSkuId();// 更新Hashpipeline.hset(cartKey, field, serialize(item));// 更新ZSETpipeline.zadd(cartKey + ":zset", System.currentTimeMillis(), field);});pipeline.sync();}
}

2. 异步队列削峰

@KafkaListener(topics = "cart_updates")
public void processCartUpdate(CartUpdateEvent event) {redisTemplate.executePipelined((RedisCallback<Object>) connection -> {event.getUpdates().forEach(update -> {connection.hSet(update.getCartKey().getBytes(),update.getField().getBytes(),serialize(update.getValue()));});return null;});
}

五、高并发读取优化

1. 热点数据预加载

@Scheduled(fixedRate = 600000) // 每10分钟执行
public void preloadActiveCarts() {List<String> activeUsers = userService.getRecentActiveUsers(10000);activeUsers.parallelStream().forEach(userId -> {String cartKey = buildCartKey(userId);Map<String, String> cartData = jedis.hgetAll(cartKey);localCache.put(userId, cartData);});
}

2. 分片读取优化

public Map<String, CartItem> getCartSharded(String userId) {String cartKey = buildCartKey(userId);List<String> fields = new ArrayList<>();// 分片读取HashMap<String, CartItem> result = new ConcurrentHashMap<>();IntStream.range(0, 4).parallel().forEach(shard -> {ScanParams params = new ScanParams().count(100).match("sku*");String cursor = "0";do {ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(cartKey, cursor, params);scanResult.getResult().forEach(entry -> {if (entry.getKey().hashCode() % 4 == shard) {result.put(entry.getKey(), deserialize(entry.getValue()));}});cursor = scanResult.getCursor();} while (!"0".equals(cursor));});return result;
}

六、实时库存校验方案

1. 库存缓存设计

// 库存Key结构
String stockKey = "stock:" + skuId + ":" + warehouseId;// 原子扣减库存
Long remain = jedis.eval("local current = redis.call('get', KEYS[1])\n" +"if not current then return -1 end\n" +"if tonumber(current) < tonumber(ARGV[1]) then return -1 end\n" +"return redis.call('decrby', KEYS[1], ARGV[1])", Collections.singletonList(stockKey), Collections.singletonList("1")
);

2. 库存预占机制

public boolean reserveStock(String userId, String skuId, int quantity) {String lockKey = "stock_lock:" + skuId;RLock lock = redissonClient.getLock(lockKey);try {if (lock.tryLock(100, 1000, TimeUnit.MILLISECONDS)) {// 检查实际库存int realStock = getRealStock(skuId);if (realStock < quantity) return false;// 写入预占记录String reserveKey = "reserve:" + userId + ":" + skuId;jedis.setex(reserveKey, 300, String.valueOf(quantity));// 更新显示库存jedis.decrBy("display_stock:" + skuId, quantity);return true;}} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}return false;
}

七、数据一致性保障

1. 双写一致性方案

App Redis MQ DB 1. 写入购物车数据 2. 发送变更事件 3. 异步持久化 4. 定时全量同步 5. 返回操作结果 App Redis MQ DB

2. 补偿对账机制

@Scheduled(cron = "0 0 2 * * ?")
public void cartReconciliation() {// 扫描所有购物车KeyScanParams params = new ScanParams().match("cart:*").count(100);String cursor = "0";do {ScanResult<String> scanResult = jedis.scan(cursor, params);scanResult.getResult().parallelStream().forEach(cartKey -> {// 对比Redis与数据库Map<String, String> redisData = jedis.hgetAll(cartKey);Map<String, CartItem> dbData = cartDAO.getFromDB(extractUserId(cartKey));if (!dataEquals(redisData, dbData)) {log.warn("数据不一致:{}", cartKey);repairData(cartKey, redisData, dbData);}});cursor = scanResult.getCursor();} while (!"0".equals(cursor));
}

八、性能压测数据

测试环境

  • Redis Cluster(6节点,32核/128GB)
  • 1000并发线程
  • 单用户购物车50件商品

性能指标

操作类型优化前性能优化后性能提升倍数
添加商品1200 TPS8500 TPS7.1x
批量删除800 TPS6800 TPS8.5x
全量获取300 QPS4500 QPS15x
库存校验1500 TPS12000 TPS8x

九、生产环境最佳实践
  1. 容量规划

    • 按每个用户购物车平均50个商品计算
    • 单个Hash存储约需5KB内存
    • 百万用户需预留:1,000,000 * 5KB = 5GB
  2. 故障应急方案

    • 熔断降级:启用本地缓存应急模式
    • 快速扩容:Redis Cluster在线扩容
    • 数据恢复:AOF+RDB双重保障
  3. 监控关键指标

    # 实时监控命令
    redis-cli info stats | grep -E "instantaneous_ops_per_sec|keyspace_hits"
    redis-cli info memory | grep used_memory_human
    redis-cli latency doctor
    

十、总结与扩展

通过本方案可实现:

  • 毫秒级响应:核心操作<10ms
  • 99.99%可用性:双机房容灾保障
  • 线性扩展:支持千万级用户购物车
  • 精准库存:实时库存校验误差<0.1%

扩展优化方向

  1. 结合CDN缓存静态化购物车页面
  2. 使用Redis Stream实现实时价格推送
  3. 引入机器学习预测用户购物行为

更多资源:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】

http://www.lryc.cn/news/2396787.html

相关文章:

  • 【计算机网络】传输层UDP协议
  • 安全漏洞修复导致SpringBoot2.7与Springfox不兼容
  • 从法律层面剖析危化品证书:两证一证背后的安全逻辑
  • C语言——获取变量所在地址(uint8和uint32的区别)
  • 2 Studying《Effective STL》
  • 深入理解复数加法与乘法:MATLAB演示
  • 【设计模式-3.6】结构型——桥接模式
  • 【前端】性能优化篇
  • 【redis实战篇】第六天
  • 力扣题解654:最大二叉树
  • 手写ArrayList和LinkedList
  • Android bindservice绑定服务,bindServiceAsUser补充
  • [蓝桥杯]交换次数
  • 95套HTML高端大数据可视化大屏源码分享
  • 系统架构设计综合知识与案例分析
  • scale up 不能优化 TCP 聚合性能
  • Python-matplotlib库之核心对象
  • Linux 脚本文件编辑(vim)
  • 学习BI---基本操作---数据集操作
  • 初学大模型部署以及案例应用(windows+wsl+dify+mysql+Ollama+Xinference)
  • AI Agent企业级生产应用全解析
  • RocketMQ 学习
  • 【前端】html2pdf实现用前端下载pdf
  • Redis部署架构详解:原理、场景与最佳实践
  • 前端开发知识体系全景指南
  • C++哈希表:unordered系列容器详解
  • vue-13(延迟加载路由)
  • pom.xml 文件中配置你项目中的外部 jar 包打包方式
  • WordPress通过简码插入bilibili视频
  • ZLG ZCANPro,ECU刷新,bug分享