Redis最佳实践——购物车优化详解
Redis在电商购物车高并发读写场景下的优化实践
一、购物车业务场景分析
-
典型操作特征
- 读/写比例 ≈ 8:2
- 高峰QPS可达10万+
- 单用户最大商品数500+
- 操作类型:增删改查、全选/反选、数量修改
-
技术挑战
- 高并发下的数据一致性
- 海量数据存储与快速访问
- 实时价格计算与库存校验
- 分布式环境下的会话管理
二、核心数据结构设计优化
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. 多级缓存架构
2. 各层缓存配置
缓存层级 | 技术选型 | 容量 | 过期策略 |
---|---|---|---|
本地缓存 | Caffeine | 10万用户 | 基于大小+访问时间(1分钟) |
Redis缓存 | Hash+Zset | 1TB内存 | 动态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. 双写一致性方案
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 TPS | 8500 TPS | 7.1x |
批量删除 | 800 TPS | 6800 TPS | 8.5x |
全量获取 | 300 QPS | 4500 QPS | 15x |
库存校验 | 1500 TPS | 12000 TPS | 8x |
九、生产环境最佳实践
-
容量规划
- 按每个用户购物车平均50个商品计算
- 单个Hash存储约需5KB内存
- 百万用户需预留:1,000,000 * 5KB = 5GB
-
故障应急方案
- 熔断降级:启用本地缓存应急模式
- 快速扩容:Redis Cluster在线扩容
- 数据恢复:AOF+RDB双重保障
-
监控关键指标
# 实时监控命令 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%
扩展优化方向:
- 结合CDN缓存静态化购物车页面
- 使用Redis Stream实现实时价格推送
- 引入机器学习预测用户购物行为
更多资源:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】
相关文章:

Redis最佳实践——购物车优化详解
Redis在电商购物车高并发读写场景下的优化实践 一、购物车业务场景分析 典型操作特征 读/写比例 ≈ 8:2高峰QPS可达10万单用户最大商品数500操作类型:增删改查、全选/反选、数量修改 技术挑战 高并发下的数据一致性海量数据存储与快速访问实时价格计算与库存校验分…...

【计算机网络】传输层UDP协议
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:计算机网络 🌹往期回顾🌹: 【计算机网络】应用层协议Http——构建Http服务服务器 🔖流水不争,争的是滔滔不…...

安全漏洞修复导致SpringBoot2.7与Springfox不兼容
项目基于 springboot2.5.2 实现的,用 springfox-swagger2 生成与前端对接的 API 文档;pom.xml 中依赖如下 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>&l…...

从法律层面剖析危化品证书:两证一证背后的安全逻辑
《安全生产法》第 24 条明确规定,危化品单位主要负责人和安全管理人员 “必须考核合格方可上岗”。这并非仅仅是行政要求,而是通过法律来筑牢安全防线。在某危化品仓库爆炸事故中,由于负责人未持证,导致事故责任升级,企…...
C语言——获取变量所在地址(uint8和uint32的区别)
前言: 1.使用uint8 *的原因 在C语言中,获取或操作一个4字节地址(指针)时使用uint8_t*(即unsigned char*)而不是uint32_t*,主要基于以下关键原因: 1.1. 避免违反严格别名规则&…...
2 Studying《Effective STL》
目录 0 引言 1 容器 1. 慎重选择容器类型 3. 确保容器中的对象副本正确且高效 4. 调用empty()而不是检查size()是否为0 5. 区间成员函数优先于与之对应的单元素成员函数 7. 如果容器中包含了通过new创建的指针,切记析构前将指针delete掉 9. 慎重选择删除元素…...

深入理解复数加法与乘法:MATLAB演示
在学习复数的过程中,复数加法与乘法是两个非常基础且重要的概念。复数的加法和乘法操作与我们常见的实数运算有所不同,它们不仅涉及到数值的大小,还有方向和相位的变化。在这篇博客中,我们将通过MATLAB演示来帮助大家更好地理解复…...

【设计模式-3.6】结构型——桥接模式
说明:本文介绍结构型设计模式之一的桥接模式 定义 桥接模式(Bridge Pattern)又叫作桥梁模式、接口(Interface)模式或柄体(Handle and Body)模式,指将抽象部分与具体实现部分分离&a…...
【前端】性能优化篇
长期更新,建议关注收藏点赞。 目录 性能优化具体指标监控工具/系统解决方案htmlcssjsvuereact包体积静态资源图片优化白屏首屏加载速度缓存优化网络优化web worker动画 面试题汇总怎么实现无限加载,如果有一亿条数据怎么优化 性能优化 本文仅是列出常见…...
【redis实战篇】第六天
摘要: 本文介绍了基于Redis的秒杀系统优化方案,主要包含两部分:1)通过Lua脚本校验用户秒杀资格,结合Java异步处理订单提升性能;2)使用Redis Stream实现消息队列处理订单。方案采用Lua脚本保证库…...

力扣题解654:最大二叉树
一、题目内容 题目要求根据一个不重复的整数数组 nums 构建最大二叉树。最大二叉树的构建规则如下: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值左边的子数组前缀上构建左子树。递归地在最大值右边的子数组后缀上构建右子树。返回由 nums 构…...
手写ArrayList和LinkedList
项目仓库:https://gitee.com/bossDuy/hand-tear-collection-series 基于b站up生生大佬:https://www.bilibili.com/video/BV1Kp5tzGEc5/?spm_id_from333.788.videopod.sections&vd_source4cda4baec795c32b16ddd661bb9ce865 LinkedList package com…...
Android bindservice绑定服务,bindServiceAsUser补充
Android bindservice绑定服务,并同步返回service对象的两个方法-CSDN博客 补充反射并调用bindServiceAsUser的方法: private boolean initService2(final Context context){if(deviceServicenull){latch new CountDownLatch(1);HandlerThread handler…...
[蓝桥杯]交换次数
交换次数 题目描述 IT 产业人才需求节节攀升。业内巨头百度、阿里巴巴、腾讯(简称 BAT )在某海滩进行招聘活动。 招聘部门一字排开。由于是自由抢占席位,三大公司的席位随机交错在一起,形如:BABTATT,这使…...

95套HTML高端大数据可视化大屏源码分享
概述 在大数据时代,数据可视化已成为各行各业的重要需求。这里精心整理了95套高端HTML大数据可视化大屏源码,这些资源采用现代化设计风格,可帮助开发者快速构建专业的数据展示界面。 主要内容 1. 设计风格与特点 采用…...
系统架构设计综合知识与案例分析
system-architect 软考高级-系统架构设计师-综合知识与案例分析:软件工程、网络工程、结构化分析方法、面向对象分析方法、软件质量数量、传统数据库、分布式数据库、系统架构等。 —— 2025 年 3 月 20 日 甲辰年二月二十一 春分 目录 system-architect1、计算机基…...

scale up 不能优化 TCP 聚合性能
scale up 作为一种系统扩展优化的方法,旨在提高系统组件的执行效率,比如替换更高性能的硬件或算法。是否可以此为依据优化 TCP 呢,例如通过多条路径聚合带宽实现吞吐优化(对,还是那个 MPTCP),答案是否定的。 因为 TCP…...

Python-matplotlib库之核心对象
matplotlib库之核心对象 FigureFigure作用Figure常用属性Figure常用方法Figure对象的创建隐式创建(通过 pyplot)显式创建使用subplots()一次性创建 Figure 和 Axes Axes(绘图区)Axes创建方式Axes基本绘图功能Axes绘图的常用参数Ax…...

Linux 脚本文件编辑(vim)
1. 用户级配置文件(~/.bashrc) vim ~/.bashrc # 编辑 source ~/.bashrc # 让编辑生效 ~/.bashrc 文件是 Bash Shell 的配置文件,用于定义用户登录时的环境变量、别名、函数等设置。当你修改了 ~/.bashrc 文件后,通常需要重新…...

学习BI---基本操作---数据集操作
什么是数据集, 数据集(Dataset) 是指从原始数据源(如数据库、Excel、API等)提取并经过标准化处理后的数据集合,通常以二维表形式存储,用于支撑报表、仪表盘等可视化分析。 数据集在QuickB…...

初学大模型部署以及案例应用(windows+wsl+dify+mysql+Ollama+Xinference)
大模型部署以及案例应用(windowswsldifymysqlOllamaXinference) 1.wsl 安装①安装wsl②测试以及更新③安装Ubuntu系统查看系统以及版本安装Ubuntu系统进入Ubuntu系统 2、docker安装①下载安装包②安装③docker配置 3、安装dify①下载dify②安装③生成.en…...
AI Agent企业级生产应用全解析
在企业级应用中,AI Agent 的核心是将其从一个对话模型转变为一个自主决策和执行的自动化工作流引擎。这需要一个精密的 “Agent 执行框架”(Agent Orchestration Framework) 来协调 LLM 的推理、外部工具的调用、记忆管理和自我反思。 AI Ag…...
RocketMQ 学习
消息队列 参考官方文档:https://rocketmq.apache.org/zh/docs/ 基本概念 主题(Topic):是消息传输和消息存储的顶级容器,不是实际的消息容器,而是一个逻辑上的概念,用于区分不同业务消息的标识&…...
【前端】html2pdf实现用前端下载pdf
npm安装完后,编写代码。 <template><div id"pdf-content">需要被捕获为pdf的内容</div> </template><script> import html2pdf from html2pdf.js;export default {methods: {downloadPdf() {const element document.getE…...

Redis部署架构详解:原理、场景与最佳实践
Redis部署架构详解:原理、场景与最佳实践 Redis作为一种高性能的内存数据库,在现代应用架构中扮演着至关重要的角色。随着业务规模的扩大和系统复杂度的提升,选择合适的Redis部署架构变得尤为重要。本文将详细介绍Redis的各种部署架构模式&a…...
前端开发知识体系全景指南
文章目录 前言前端开发者知识体系清单一、JavaScript基础变量和类型原型和原型链作用域和闭包执行机制语法和API 二、HTML和CSSHTMLCSS手写 三、计算机基础编译原理网络协议设计模式 四、数据结构和算法JavaScript编码能力手动实现前端轮子数据结构算法 五、运行环境浏览器API浏…...

C++哈希表:unordered系列容器详解
本节目标 1.unordered系列关联式容器 2.底层结构 3.模拟实现 4.哈希的应用 5.海量数据处理面试题 unordered系列关联式容器 在c98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可以达到logN,即最差的情况下需要比较红…...
vue-13(延迟加载路由)
用于性能优化的延迟加载路由 延迟加载路由是优化 Vue.js 应用程序性能的关键技术,尤其是那些具有大量路由的应用程序。通过仅在实际需要时加载路由组件,您可以显著减少应用程序的初始加载时间,从而获得更好的用户体验。这对于网络连接速度较…...
pom.xml 文件中配置你项目中的外部 jar 包打包方式
使用 system 作用域(不推荐,但简单直接) <dependency><groupId>com.test</groupId> <!-- 可自定义,建议与项目相关 --><artifactId>open-sdk</artifactId> <!-- 可自定义,建议…...

WordPress通过简码插入bilibili视频
发布于:Eucalyptus-Blog 一、前言 B站是国内非常受欢迎的视频分享平台,上面不仅内容丰富,而且很多视频制作精良、趣味十足。很多人,比如我,就喜欢将B站的视频通过 iframe 嵌入到自己的网页中,但这段代码又…...