在电商系统中,哪些数据需要放到缓存?哪些数据需要放到DB永存呢?

先给结论

有需要被"记住"而非仅仅被"看到"的信息。 用户在淘宝上的每一次点击可以被遗忘(缓存过期),但他的每一笔交易必须被铭记(DB永存)。这就是工业级系统设计中"性能"与"正确"之间的永恒权衡。

我们必须先建立一个认知:缓存是“便签纸”,关系型数据库才是“法律账本”。

便签纸(缓存)写得快、看得快,但可能写错、可能丢失、可能被风吹走;而法律账本(DB)写得慢,但它是唯一具备法律效力、绝对准确、永久保存的依据。

下面我以 “用户在淘宝下单买一件衣服” 的全流程为例,详细拆解哪些数据必须落DB,哪些放缓存,以及为什么。

1. 核心原则:什么决定了数据存哪里?

判定维度放关系型数据库 (PolarDB/OceanBase)放缓存 (Tair/Redis)
一致性要求强一致(绝对不能错)最终一致(允许短暂不准)
数据生命周期永久保存临时/有TTL
业务后果错了=资损/法律风险/客诉错了=体验下降/可重试
操作类型复杂事务、关联查询、聚合统计简单KV读写、高频计数
数据价值核心资产加速副本

2. 购物全流程数据归属详解

场景一:浏览商品详情页
数据项存储位置原因
商品标题、描述、SKU属性缓存 (主) + DB (备)读多写少,缓存命中率高;修改时异步刷新缓存
商品主图/视频CDN + OSS大文件,不适合进KV缓存
实时价格缓存 + DB价格变更时通过消息队列秒级刷新缓存;DB是定价权威来源
实时库存缓存 (扣减) + DB (校准)高并发下用Redis原子扣减防超卖;订单创建成功后异步落DB持久化
评论列表缓存 + Lindorm评论内容存Lindorm(宽表),热门评论摘要进Redis
"猜你喜欢"推荐缓存推荐引擎预计算结果,纯读场景

关键点:库存的"双写"逻辑
用户点击"立即购买"时,先在Redis中执行DECR扣减库存(毫秒级响应,抗住秒杀流量)。但这只是"预扣",真正的库存变更必须在订单创建事务中写入PolarDB。如果订单取消或支付超时,Redis和DB都要回滚。Redis是"挡箭牌",DB是"真账本"。

场景二:提交订单
数据项存储位置原因
订单主表PolarDB/OceanBase核心交易数据,涉及资金、履约、售后,必须强一致+永久存储
订单明细(SKU/数量/单价)PolarDB/OceanBase与主表在同一事务中写入,保证原子性
优惠快照(用了什么券/满减)PolarDB/OceanBase退款/审计时必须精确还原当时优惠规则
收货地址快照PolarDB/OceanBase用户后续修改地址不影响历史订单
订单号生成Tair(序列号) + DBRedis自增生成全局唯一ID,DB记录映射关系
购物车状态缓存 (主)购物车是"临时意图",非正式交易;DB仅做异步持久化防丢失

为什么订单必须落DB?

  • 事务性:扣库存、创建订单、扣优惠券、记积分,这4个操作要么全成功要么全失败,只有DB支持ACID事务。
  • 可追溯:3个月后用户投诉"我没收到货",客服需要从DB查出完整订单链路。缓存早就过期了。
  • 合规性:电商法要求交易记录保存至少3年,缓存无法承担此法律责任。
场景三:支付与履约
数据项存储位置原因
支付流水OceanBase资金数据,金融级强一致,RPO=0
支付状态回调DB + 缓存DB持久化;缓存更新订单状态供前端快速轮询
物流单号/轨迹DB + 缓存DB存权威记录;缓存存最新轨迹供高频查询
发票信息PolarDB税务合规,不可变
风控决策结果缓存实时拦截用,事后分析从日志/数仓取
场景四:用户相关数据
数据项存储位置原因
用户名/手机号/实名信息PolarDB核心身份数据,加密存储,强一致
Session/Token缓存登录态是临时的,过期即失效
用户标签(偏好/等级)缓存 + DB画像系统预计算写缓存;DB存原始行为数据
浏览/搜索历史缓存(近期) + Lindorm(长期)近期行为用于实时推荐;长期行为用于离线分析
收藏夹缓存 + DB高频读走缓存;DB保证换设备后数据不丢

3. "强一致"到底意味着什么?

以支付为例,对比两种一致性模型:

维度强一致 (OceanBase/PolarDB)最终一致 (Redis缓存)
写入后读取立即读到最新值可能读到旧值(延迟ms~s级)
故障恢复RPO=0,数据零丢失可能丢失未持久化的写入
跨行/跨表支持分布式事务不支持
适用场景转账、下单、库存确权展示、计数、会话、预扣
代价延迟较高(ms级),吞吐受限延迟极低(μs级),吞吐极高

一句话总结
"强一致" = 在任何时刻、任何节点、任何故障场景下,读到的数据都等于最后一次成功写入的值。 这是用性能和复杂度换来的"绝对正确",只用在"错了就出大事"的地方。

4. 架构全景图:一次购物的数据流转

用户点击"购买" │ ▼ ┌──────────────┐ 预扣库存 ┌─────────────┐ │ 前端/App │ ─────────────→ │ Tair(Redis) │ ← 毫秒级响应,抗并发 └──────────────┘ └─────────────┘ │ │ │ 创建订单(同步) │ 异步同步 ▼ ▼ ┌──────────────────────────────────────────────┐ │ PolarDB / OceanBase │ │ ┌─────────┐ ┌─────────┐ ┌────────────────┐ │ │ │订单表 │ │库存表 │ │优惠/支付流水表 │ │ │ │(强一致) │ │(强一致) │ │(强一致) │ │ │ └─────────┘ └─────────┘ └────────────────┘ │ │ ↑ ACID事务保证原子性 │ └──────────────────────────────────────────────┘ │ │ 订单创建成功 → 发消息 ▼ ┌──────────────┐ │ 消息队列 │ → 通知缓存更新订单状态 │ (RocketMQ) │ → 触发积分/通知等异步任务 └──────────────┘

终极总结

问题答案
哪些必须落DB?一切涉及钱、权、责、法的数据:订单、支付、库存确权、用户身份、合同、发票
哪些放缓存?一切为了快、省、扛的数据:商品展示、Session、预扣库存、推荐结果、热点配置
两者关系?DB是"真相之源",缓存是"高性能投影"。缓存可以脏、可以丢、可以过期,但DB永远是对的
为什么不全放DB?DB扛不住百万QPS,延迟也满足不了用户体验
为什么不全放缓存?缓存没有事务、不能持久、无法追责,用来管钱等于裸奔