缓存更新策略选型:交易场景下的四种模式深度对比
大家好,我是你们的老朋友。今天咱们聊聊缓存更新策略。
说实话,在交易系统里,缓存一致性是个绕不开的坎。我见过太多团队,上来就用Redis,结果数据对不上,半夜被运维叫起来修数据。嗯,今天我就把这四种模式掰开揉碎了讲清楚。
一、Cache-Aside(旁路缓存)
这是最常用的模式,也是我个人的首选。说白了,就是应用程序自己管缓存。
读流程:先查缓存,有就直接返回。没有?查数据库,然后塞进缓存。
写流程:先更新数据库,再删除缓存。
核心要点:为什么是删除缓存而不是更新?
因为并发写场景下,更新缓存会导致数据不一致。我踩过这个坑,后来就老实了。
// 伪代码示例
public Order getOrder(String orderId) {
Order order = cache.get(orderId);
if (order == null) {
order = db.queryOrder(orderId);
cache.set(orderId, order);
}
return order;
}
public void updateOrder(Order order) {
db.updateOrder(order);
cache.del(order.getOrderId()); // 删除缓存
}
我的经验:在订单系统中,Cache-Aside配合延迟双删效果更好。先删缓存,再更新数据库,过几百毫秒再删一次。虽然有点笨,但确实管用。
二、Read-Through(通读模式)
这个模式把读操作封装到了缓存层。应用程序只跟缓存打交道,缓存自己负责从数据库加载数据。
听起来很美好对吧?但我在项目中用过一次就放弃了。为什么?
- 缓存层需要实现复杂的加载逻辑
- 交易系统的查询条件太灵活,很难抽象
- 出了问题不好排查,你想想看,到底是缓存挂了还是数据库慢了?
避坑指南:我曾经在一个风控系统里用Read-Through,结果缓存雪崩时,所有请求都穿透到数据库,直接把MySQL打挂了。后来我改成Cache-Aside,至少能控制并发。
三、Write-Through(通写模式)
写操作先经过缓存,缓存同步写数据库。数据一致性很高,但延迟也高。
适合什么场景?
- 对一致性要求极高的场景,比如账户余额
- 写并发不高的场景
不适合什么?
- 高并发交易系统。每次写都要等数据库确认,延迟受不了
- 批量写场景。我见过有人用Write-Through做订单批量更新,结果性能惨不忍睹
// Write-Through 示意
public void writeThrough(String key, Order order) {
cache.set(key, order); // 先写缓存
db.updateOrder(order); // 同步写数据库
// 只有两个都成功才算完成
}
四、Write-Behind(异步写回)
这个模式很有意思。数据先写到缓存,然后异步批量写到数据库。
说白了,就是拿一致性换性能。
| 优势 | 劣势 |
|---|---|
| 写性能极高,适合高并发 | 数据可能丢失(缓存挂了) |
| 可以批量合并写操作 | 一致性窗口期长 |
| 减少数据库压力 | 实现复杂,需要持久化队列 |
我的建议:交易系统里慎用Write-Behind。除非你能接受数据丢失,比如日志、统计等非核心数据。核心的订单、余额,千万别用。
五、四种模式对比总结
我画了一张图,帮你快速理解这四种模式的核心区别:
六、交易场景选型建议
说了这么多,到底怎么选?我直接给结论:
- 订单查询:Cache-Aside,配合本地缓存做二级缓存
- 账户余额:Write-Through,一致性优先
- 交易流水:Write-Behind,允许秒级延迟
- 商品库存:Cache-Aside + 分布式锁,防止超卖
我的小技巧:别死磕一种模式。交易系统里往往是混合使用。比如订单用Cache-Aside,日志用Write-Behind。灵活搭配才是王道。
好了,这四种模式就讲到这里。记住一句话:没有银弹,只有最适合你场景的方案。下节课咱们聊聊缓存穿透、击穿、雪崩的实战处理,到时候见。