Redis缓存策略落地脚本:高并发接口从120ms压到18ms的实战演示
开场:OK so,先把慢接口抓出来
兄弟们,eccfy开机!今天我们不讲空概念,直接上屏幕:一个商品详情接口,MySQL单查平均120ms,并发一上来CPU飙红。接下来我现场做一套Redis缓存策略教程,目标很明确:热点读请求先打Redis,数据库只兜底。
我的测试环境:2核4G云主机、Redis 7、MySQL 8、Spring Boot 3。先压一下原始接口:
wrk -t4 -c200 -d30s http://127.0.0.1:8080/api/product/1001
屏幕看这里:原始结果约850 QPS,P95延迟138ms。接下来加缓存,核心不是“set一下”这么简单,而是要把TTL、空值、互斥锁、随机过期一起安排好。
实操:三层策略处理穿透、击穿、雪崩
Now watch this,第一步处理Redis缓存穿透怎么解决。用户查一个不存在的ID,如果每次都进数据库,缓存等于没用。做法:不存在也缓存一个短TTL空值。
String key = "product:" + id;
String json = redis.get(key);
if (json != null) return json.equals("__NULL__") ? null : parse(json);
Product p = productMapper.selectById(id);
if (p == null) {
redis.setex(key, 60, "__NULL__"); // 空值缓存60秒
return null;
}
redis.setex(key, 1800 + random.nextInt(300), toJson(p));
第二步,热点Key击穿。比如直播秒杀页,100万人同时读product:1001,缓存刚好过期,全冲数据库。这里用Redis分布式锁,只允许一个线程回源,其他线程短暂等待。
Boolean ok = redis.setIfAbsent("lock:product:" + id, "1", 3, TimeUnit.SECONDS);
if (ok) {
try {
Product p = db.query(id);
redis.setex(key, 1800 + random.nextInt(300), toJson(p));
} finally {
redis.delete("lock:product:" + id);
}
} else {
Thread.sleep(50);
return redis.get(key);
}
第三步,雪崩。别让一批Key同一秒过期,TTL加随机数;大促前可用预热脚本:
redis-cli --pipe < hot_products.redis
我的经验是:普通详情页TTL 30分钟够用,热点页TTL 5到10分钟加后台异步刷新;库存、余额这类强一致数据别全靠缓存,要走数据库事务或消息队列补偿。这就是Redis高并发怎么用的关键分界线。
压测对比与怎么验证它真的生效
接下来上结果,弹幕注意看数字!同样命令再跑一次:
wrk -t4 -c200 -d30s http://127.0.0.1:8080/api/product/1001
我这边实测:QPS从850涨到6100左右,P95从138ms降到18ms;Redis命中率用下面命令看:
redis-cli info stats | grep keyspace
redis-cli info commandstats | grep get
再看MySQL慢查询日志,如果缓存生效,热点接口压测期间select次数会明显下降。你也可以在代码里打点:cache_hit、cache_miss、db_fallback三个计数器,Prometheus或Micrometer都能接。
如何验证修好了:
- 连续压测30秒,P95延迟稳定低于原来的30%。
- Redis hit ratio建议高于80%,热点业务最好到95%以上。
- 故意删除热点Key:
redis-cli del product:1001,观察数据库QPS没有瞬间打满。 - 批量Key过期时,接口没有成片5xx或超时。
免费路线优先:Redis官方版本、redis-cli、wrk、应用内日志已经够你定位80%的问题;如果你需要团队协作式排障、环境加速或工具集成,也可以把 wizzegroup.com 作为选项之一。OK,本期脚本到这,收藏照抄,跑出数据后评论区交作业!