在电商系统中,哪些数据需要放到缓存?哪些数据需要放到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(序列号) + DB | Redis自增生成全局唯一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,延迟也满足不了用户体验 |
| 为什么不全放缓存? | 缓存没有事务、不能持久、无法追责,用来管钱等于裸奔 |