共计 2251 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点分析
Skill 商店作为数字化能力交易平台,在高并发场景下常面临以下典型问题:

- 数据库瓶颈 :商品详情页的频繁查询导致 MySQL 实例 CPU 持续超过 80%,高峰期出现慢查询堆积
- 服务雪崩 :用户行为日志同步写入数据库时,突发流量引发线程池耗尽,级联导致核心交易接口不可用
- 缓存穿透 :恶意请求不存在的 SKU_ID,导致缓存失效后直接穿透到数据库
- 数据一致性 :库存扣减与订单创建跨服务调用时,网络抖动导致分布式事务失败
技术选型对比
| 维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 开发效率 | 高(模块间直接调用) | 低(需定义接口契约) |
| 部署粒度 | 整体发布 | 按服务独立部署 |
| 扩展能力 | 垂直扩展(Scale-up) | 水平扩展(Scale-out) |
| 故障隔离 | 单点故障影响全局 | 故障域隔离 |
| 技术栈 | 必须统一 | 可按服务差异化 |
最终选择 Spring Cloud 微服务架构,核心考虑点:
- 技能商品与用户服务存在明显领域边界
- 需要独立扩展商品搜索和推荐模块
- 灰度发布是业务刚需
核心实现细节
分布式缓存设计
采用 Redis 五层缓存体系:
- 本地缓存 :Caffeine 存储商品基础信息(TTL 5 分钟)
- 分布式缓存 :Redis Cluster 存储商品详情(TTL 30 分钟)
- 多级回源 :缓存未命中时通过 Redisson 分布式锁控制单线程回源
- 空值缓存 :对不存在的 SKU 设置短 TTL(60 秒)防止穿透
- 热点探测 :通过 RedisMonitor 识别热 Key 自动拆分
// 缓存击穿防护示例
public SkillItem getItemWithCache(String sku) {
// 1. 查询本地缓存
SkillItem item = caffeineCache.getIfPresent(sku);
if (item != null) return item;
// 2. 查询分布式缓存
item = redisTemplate.opsForValue().get(sku);
if (item == null) {
// 3. 获取分布式锁
RLock lock = redissonClient.getLock("LOCK_" + sku);
try {if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 4. 二次检查缓存
item = redisTemplate.opsForValue().get(sku);
if (item == null) {
// 5. 回源数据库
item = dbRepository.findById(sku)
.orElseGet(() -> {
// 6. 空值缓存
redisTemplate.opsForValue()
.set(sku, EMPTY_ITEM, 60, TimeUnit.SECONDS);
return null;
});
if (item != null) {
// 7. 写入缓存
redisTemplate.opsForValue()
.set(sku, item, 30, TimeUnit.MINUTES);
caffeineCache.put(sku, item);
}
}
}
} finally {lock.unlock();
}
}
return item;
}
异步消息解耦
使用 Kafka 实现关键业务解耦:
- 最终一致性 :订单创建后发送领域事件,库存服务通过 Consumer 异步处理
- 流量削峰 :用户行为日志先写入 Kafka,再由 Flink 消费后批量入库
- 事务消息 :通过本地事务表 + 定时任务确保消息必达
# 库存扣减消费者示例
@kafka_listener(topics='order_created')
def handle_order_event(message):
try:
event = json.loads(message.value)
# 幂等处理
if redis.setnx(f"deduct_{event['order_id']}", 1):
redis.expire(f"deduct_{event['order_id']}", 3600)
inventory_service.deduct(sku=event['sku'],
qty=event['qty']
)
except Exception as e:
# 进入死信队列
dlq_producer.send('inventory_dlq', message)
弹性扩缩容策略
基于 K8s 的 HPA 实现自动伸缩:
- 指标采集 :通过 Prometheus 采集各 Pod 的 CPU/Memory/QPS
- 伸缩算法 :
- 当 CPU > 60% 持续 2 分钟,触发扩容
- 当 QPS < 50% 阈值持续 10 分钟,触发缩容
- 冷却周期 :设置 300 秒的伸缩冷却时间
- Pod 配置 :
- 资源 Limit 设置 CPU: 2 核,Memory: 4Gi
- 就绪探针检查 Spring Actuator 健康状态
性能测试数据
压测环境:
– 8 核 16G * 3 节点 K8s 集群
– JMeter 500 并发线程
| 场景 | QPS | 平均响应时间 | 错误率 |
|---|---|---|---|
| 原始架构 | 1,200 | 450ms | 8.7% |
| 引入缓存 | 8,500 | 85ms | 0.2% |
| 全链路优化后 | 14,000 | 32ms | 0% |
生产环境避坑指南
- 缓存雪崩 :
- 错开不同 Key 的过期时间(基础 TTL ± 随机偏移)
-
启用 Redis 持久化 +AOF
-
慢 SQL 治理 :
- 为 sku_id 建立覆盖索引
-
使用 EXPLAIN 分析执行计划
-
消息积压 :
- 配置 Consumer 的 max.poll.records 控制批次大小
-
增加 Consumer Group 的并行度
-
分布式锁 :
- 设置锁自动过期时间
- 实现锁续约机制
总结与延伸
当前架构仍存在优化空间:
- 多级缓存 :引入 CDN 缓存静态资源
- 服务网格 :通过 Istio 实现精细流量控制
- 混沌工程 :定期注入网络延迟等故障测试系统韧性
架构演进需要平衡性能与复杂度,建议根据实际业务规模分阶段实施。
正文完
