共计 1570 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点分析
虾评 skill 作为一个用户互动频繁的系统,在高并发场景下主要面临以下核心问题:

- 响应延迟飙升 :当热点内容出现时,DB 查询量激增导致接口响应时间从 200ms 恶化到 2s+
- 数据一致性挑战 :用户点赞 / 取消操作在集群环境下出现计数偏差
- 系统雪崩风险 :某个服务节点故障引发连锁反应,导致整个服务不可用
我们通过监控发现,在晚高峰时段:
– 核心接口 QPS 突破 8000+
– MySQL CPU 利用率持续高于 90%
– 缓存命中率不足 60%
技术方案设计
同步 vs 异步处理对比
- 同步模式 (改造前):
- 优点:实现简单,强一致性保证
-
缺点:线程阻塞导致吞吐量天花板明显
-
异步模式 (优化方案):
- 通过消息队列解耦
- 采用最终一致性模型
- 吞吐量提升 3 倍以上
Redis 缓存设计
分层缓存策略:
1. 一级缓存:本地 Caffeine(100ms 级过期)
2. 二级缓存:Redis 集群(5 分钟过期)
3. 缓存 key 设计:biz:content:{id}:stats
消息队列选型
| 维度 | Kafka | RabbitMQ |
|---|---|---|
| 吞吐量 | 10W+/s | 5W/s |
| 延迟 | 毫秒级 | 微秒级 |
| 适用场景 | 日志流处理 | 业务消息 |
最终选择 RabbitMQ 因为:
– 需要更灵活的路由规则
– 消息优先级支持更好
核心实现细节
缓存击穿防护
// 使用 Redisson 实现分布式锁
public ContentStats getContentStats(Long contentId) {
String cacheKey = "biz:content:" + contentId + ":stats";
ContentStats stats = redisTemplate.opsForValue().get(cacheKey);
if (stats == null) {RLock lock = redissonClient.getLock("lock:" + cacheKey);
try {if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 双重检查
stats = redisTemplate.opsForValue().get(cacheKey);
if (stats == null) {stats = dbQuery(contentId);
redisTemplate.opsForValue().set(cacheKey, stats, 5, TimeUnit.MINUTES);
}
}
} finally {lock.unlock();
}
}
return stats;
}
消息幂等处理
def handle_like_msg(msg):
msg_id = msg['message_id']
if redis_client.setnx(f"msg_dedup:{msg_id}", 1):
redis_client.expire(f"msg_dedup:{msg_id}", 86400)
# 实际业务处理
update_like_count(msg['content_id'])
架构拓扑
客户端 -> Nginx -> API 集群
↓
Redis 集群
↓
RabbitMQ 集群
↗ ↖
Worker 集群 MySQL 集群
性能验证
压测环境
- 硬件:8C16G 云服务器×5
- 工具:JMeter 5.4
- 场景:模拟用户点赞高峰
关键指标
| 指标 | 优化前 | 优化后 |
|---|---|---|
| QPS 峰值 | 3,200 | 12,800 |
| 平均响应时间 | 450ms | 85ms |
| 错误率 | 3.2% | 0.1% |
避坑指南
- 分布式锁使用
- 必须设置合理的超时时间
- 推荐使用 Redisson 的看门狗机制
-
避免锁粒度过粗
-
缓存雪崩预防
- 过期时间添加随机值(基础 5 分钟±30 秒)
- 采用多级缓存策略
-
热点数据永不过期
-
消息堆积应对
- 监控队列长度设置阈值告警
- 动态扩容 Consumer 实例
- 紧急情况启用降级策略
总结与展望
当前方案仍存在两个主要局限:
1. 跨机房同步延迟问题
2. 冷启动时缓存预热耗时
下一步优化方向:
– 尝试采用 Redis6 的 Client-side caching
– 引入 CDN 加速静态内容
– 探索分片批处理模式
正文完
