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

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. 双写一致性方案

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

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. 引入机器学习预测用户购物行为
http://www.dtcms.com/a/392997.html

相关文章:

  • Netty从0到1系列之Netty内存管理【下】
  • 【使用函数求余弦COS函数的近似值】2022-11-27
  • 前端违规页面车主信息优化说明
  • 成功安装了 Anaconda3。要启动它,您有以下几种主要方式:方式一:通过“开始菜单”启动(最直接的方法)1. 点击您电脑屏幕左下角的 “开始菜单”(Win
  • flex布局实现导航栏横向滚动切换
  • 改进过程缺乏数据驱动会带来哪些后果
  • 实验1.1点亮led灯
  • 林粒粒的视频笔记13-数据清洗
  • Java进阶教程,全面剖析Java多线程编程,线程出让,笔记09
  • 大模型微调之 用LoRA微调Llama2(附代码)——李宏毅2025大模型作业5笔记-上
  • Matplotlib地理数据可视化技术详解:Cartopy与Basemap实战指南
  • wordpress 图片不显示 后台无法登陆的问题之一
  • TFS-2023《Local-Global Fuzzy Clustering With Anchor Graph》
  • Spring —— AOP
  • 讲一下ZooKeeper的持久化机制
  • 【Java后端】深入理解 Spring Security:从原理到实战
  • LeetCode:31.K个一组翻转链表
  • openharmony之系统亮度范围定制
  • 一种利用串口51单片机远程升级 OTA
  • Redis三种集群模式
  • C++ map_set封装
  • NW836NW884美光固态闪存NW885NW913
  • STM32计算步进电机转速
  • liboffice 全屏禁用工具栏
  • Photoshop - Photoshop 调整图像品质
  • 【CF】Day146——杂题 (递归 | 规律与操作)
  • PyTorch 中特征变换:卷积与转置卷积
  • HashMap底层原理详解:扩容、红黑树与ConcurrentHashMap的线程安全
  • autodl文件存储,文件同步,conda环境同步问题
  • 【ROS2】Begginer : CLI tools - 理解 ROS 2 话题