23、缓存策略:Redis应用场景、缓存穿透/击穿/雪崩、本地缓存、分布式缓存
做市商系统里,缓存不是锦上添花,是命根子。
我见过太多团队,上来就怼Redis,结果行情一波动,系统先崩了。说白了,缓存策略没想清楚,再牛的硬件也扛不住。
今天咱们就聊聊,在结构化产品做市商系统里,缓存到底该怎么玩。
一、Redis在量化系统里的应用场景
Redis这东西,快是真快,但别啥都往里塞。我个人习惯,只放三类数据:
- 行情快照:比如当前最优买卖价、最新成交价。这些数据变化快,但不需要持久化。
- 风控参数:比如最大持仓量、单笔下单上限。这些数据读多写少,放Redis里能扛住高并发查询。
- 会话状态:比如用户登录token、交易session。用Redis做分布式session,比用数据库靠谱多了。
核心原则:Redis只存“丢了还能重建”的数据。像订单记录、成交明细这种,必须落库。
我在项目中遇到过一个问题:行情推送频率太高,Redis写压力巨大。后来我们做了个策略——合并写入。比如1秒内同一个品种的行情,只写最后一条。效果立竿见影,CPU直接降了30%。
二、缓存穿透、击穿、雪崩——三个要命的坑
这三个词,面试常考,实战更要命。咱们一个一个说。
1. 缓存穿透
啥意思?就是查一个根本不存在的数据。比如用户查一个不存在的订单号,缓存里没有,数据库里也没有。每次请求都穿透到数据库,要是有人恶意攻击,数据库直接被打挂。
解决方案:
- 缓存空值:查不到数据时,也缓存一个空对象,设置短过期时间(比如30秒)。
- 布隆过滤器:在缓存前面加一层过滤器,判断数据是否存在。不存在就直接返回,不用查数据库。
我的经验:布隆过滤器有误判率,但做市商系统里,偶尔误判一次影响不大。我一般把误判率设在1%左右,性能最优。
2. 缓存击穿
这个更常见。某个热点key突然失效,大量请求同时打到数据库。比如某个热门期权合约的行情,缓存刚好过期,所有查询瞬间涌向数据库。
解决方案:
- 互斥锁:第一个请求发现缓存失效,加锁去查数据库,其他请求等待。等数据写回缓存后,再放行。
- 逻辑过期:缓存永不过期,但数据里带一个过期时间字段。后台异步更新,保证数据新鲜。
注意:互斥锁在高并发下可能引发死锁。我习惯用Redis的SETNX命令实现分布式锁,配合超时机制,稳妥。
3. 缓存雪崩
这是最狠的。大量缓存同时失效,数据库瞬间被压垮。比如所有行情数据都设置了相同的过期时间,一到整点,全部失效。
解决方案:
- 过期时间随机化:在基础过期时间上,加一个随机值(比如1-5分钟)。避免集体失效。
- 多级缓存:本地缓存+分布式缓存,层层防护。
- 限流降级:数据库扛不住时,直接返回旧数据,或者返回错误提示。
我曾经吃过这个亏。有一次上线,忘了给缓存加随机过期时间,结果整点一到,系统响应时间从2ms飙到2秒。嗯,从那以后,我每次上线前都会检查过期时间配置。
三、本地缓存 vs 分布式缓存
很多新手会问:有了Redis,为啥还要本地缓存?
你想想看,Redis再快,也有网络开销。本地缓存直接走内存,速度是纳秒级的。但本地缓存也有问题——数据不一致。
| 对比项 | 本地缓存 | 分布式缓存(Redis) |
|---|---|---|
| 速度 | 极快(纳秒级) | 快(毫秒级) |
| 容量 | 受限于单机内存 | 可扩展,容量大 |
| 数据一致性 | 各节点可能不一致 | 全局一致 |
| 适用场景 | 高频访问、允许短暂不一致 | 全局共享、强一致性要求 |
我做市商系统里,用的是两级缓存策略:
- 第一级:本地缓存(Caffeine),存最热的数据,比如当前最优报价。过期时间设得很短,比如1秒。
- 第二级:Redis,存次热的数据,比如历史行情快照。过期时间设得长一些,比如5分钟。
查询时,先查本地缓存,没命中再查Redis,最后才查数据库。这样,90%的请求在本地缓存就解决了。
避坑指南:本地缓存一定要设置最大容量,防止内存溢出。我曾经遇到过本地缓存撑爆了JVM堆内存,导致Full GC频繁。后来用Caffeine的maximumSize限制,再也没出过问题。
四、知识体系总览
下面这张图,是我自己总结的缓存策略全景。你看一眼,心里就有数了。
这张图把缓存策略的核心脉络理清了。你照着这个结构去设计,基本不会出大问题。
最后说一句:缓存策略没有银弹。本地缓存快但不一致,Redis一致但有网络开销。关键是根据业务场景做取舍。做市商系统里,行情数据允许毫秒级不一致,但订单数据必须强一致。搞清楚这个,你的缓存设计就成功了一半。
无相订单流研究社 微信Lucian808555