
双11的零点峰值,商品服务系统(Item Service)承受着每秒数十万次的查询(QPS)和数万次的写入(TPS)。这不再是简单的"查数据库返回JSON",而是分布式系统中数据一致性、缓存抗量与熔断降级的极限博弈。
SpringBoot以其"约定优于配置"的理念,结合Spring Cloud生态,成为了构建这类系统的工业级标准。本文将带领你从领域模型设计、缓存策略、异步削峰以及限流熔断四个维度,构建一套可支撑千万级SKU(库存量单位)的商品服务系统。
商品服务绝非简单的Item表。在双11场景下,我们需要区分基本信息(标题、图文详情)、价格库存(实时变动)、扩展属性(规格参数)以及预售/活动标识。
单一MySQL库绝对无法承受双11流量。我们采用ShardingSphere-JDBC进行数据分片:
item_id,采用Hash取模分库(16库*64表)。item_detail(富文本详情)这种大字段单独存储至OSS(对象存储服务) 或MongoDB,MySQL仅存储高频查询字段(ID、标题、价格、状态)。@Entity
@Table(name = "item")
@ShardingRule(shardColumn = "item_id", databaseStrategy = "hash_mod")
public class Item {
@Id
private Long id;
private String title;
private Long priceInCents; // 以分为单位,避免浮点误差
private Integer stock; // 实时库存(由Redis维护,DB最终一致性)
private Long categoryId;
@Column(columnDefinition = "json")
private String extraAttributes; // 使用MySQL 8.0+ JSON类型存储动态属性
}双11查询维度多,但索引并非越多越好。我们遵循"只建必要索引"原则:
item_id主键。category_id + status建立联合索引,用于后台运营列表查询。title或detail大字段上建立普通索引,此类模糊搜索应交给Elasticsearch完成。商品服务是典型的"读多写少"场景。若每次查询都穿透到数据库,系统将瞬间崩溃。
在SpringBoot中,利用@Cacheable结合Caffeine(本地缓存),将热点TOP 1000的商品数据缓存在JVM堆内,响应时间降至微秒级。
@Configuration
public class CacheConfig {
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("hotItems");
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000) // 仅缓存1000个热点
.expireAfterWrite(5, TimeUnit.MINUTES) // 5分钟过期
.recordStats()); // 便于监控命中率
return cacheManager;
}
}
@Service
public class ItemService {
@Cacheable(value = "hotItems", key = "#itemId", unless = "#result == null")
public Item getHotItem(Long itemId) {
// 本地缓存未命中时,查询Redis或DB
return this.loadFromRedisOrDB(itemId);
}
}当本地缓存过期,且大量请求同时打到Redis时,需防止缓存击穿(热点Key失效)。
public Item loadFromRedisOrDB(Long itemId) {
// 1. 查Redis
String json = redisTemplate.opsForValue().get("item:" + itemId);
if (json != null) return JSON.parseObject(json, Item.class);
// 2. 缓存未命中,尝试获取分布式锁
String lockKey = "lock:item:" + itemId;
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
try {
// 再次检查缓存(double check)
Item item = itemMapper.selectById(itemId);
redisTemplate.opsForValue().set("item:" + itemId, JSON.toJSONString(item), 1, TimeUnit.HOURS);
return item;
} finally {
redisTemplate.delete(lockKey);
}
} else {
// 未获取到锁,休眠重试
Thread.sleep(50);
return loadFromRedisOrDB(itemId);
}
}双11最核心且最棘手的业务是库存扣减。直接使用UPDATE stock SET stock = stock - 1 WHERE item_id = ? AND stock > 0在高并发下会导致行锁争用,极大降低吞吐量。
我们采用"预扣+确认"模式,将扣减操作异步化,实现最终一致性:
DECR扣减预库存,快速返回"下单成功"。@RestController
public class OrderController {
@PostMapping("/order")
public Result createOrder(@RequestBody OrderRequest request) {
// 1. Redis预扣库存(原子操作)
Long remaining = redisTemplate.opsForValue().decrement("stock:" + request.getItemId());
if (remaining < 0) {
// 库存不足,回滚Redis加回
redisTemplate.opsForValue().increment("stock:" + request.getItemId());
return Result.fail("库存不足");
}
// 2. 发送异步消息(事务消息半消息)
TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
"stock-topic",
buildStockMessage(request),
null
);
return Result.success("订单已提交,等待扣库存确认");
}
}双11流量是瞬间爆发的,即便架构再优秀,也必须具备优雅降级能力。我们引入Sentinel实现流量治理。
商品详情页是流量集中点。使用Sentinel的热点参数限流(ParamFlow),对itemId进行QPS维度限制,防止单一爆款商品拖垮整个服务。
@GetMapping("/item/{itemId}")
@SentinelResource(value = "getItem", blockHandler = "handleBlock")
public Item getItem(@PathVariable Long itemId) {
return itemService.getHotItem(itemId);
}
// 降级处理方法
public Item handleBlock(Long itemId, BlockException ex) {
// 返回默认兜底数据(如"商品火爆,请稍后重试")
return Item.defaultFallback(itemId);
}当依赖的外部服务(如价格服务)错误率超过阈值时,自动熔断,不再发起调用,直接返回本地缓存或默认值。SpringBoot中可通过@HystrixCommand或Sentinel的DegradeRule实现。
在分布式环境中,排查问题必须依赖日志、链路、指标三大支柱。
traceId,并贯穿所有线程和RPC调用,确保通过一个ID串联起所有日志。Micrometer将SpringBoot的jvm、tomcat.threads、redis.pool指标暴露给Prometheus,在Grafana配置预警大盘,当gc.time或db.wait超过阈值时自动告警。上线前的全链路压测是必不可少的。通常使用JMeter模拟百万用户并发。
application.yml中,设置server.tomcat.max-threads=200(非越大越好,取决于CPU核数),并开启max-connections=10000。maximum-pool-size为CPU核数的2倍左右,避免上下文切换开销。-XX:MaxGCPauseMillis=100以优化GC停顿。通过SpringBoot及其生态构建的双11商品服务系统,本质上是一套"有损服务"哲学的实现。我们无法保证每一笔请求都完美无瑕,但通过缓存抗量、异步解耦、限流熔断和可观测性,我们确保了系统在极限压力下不宕机、可追溯、快恢复。
这不仅是技术的胜利,更是工程权衡的艺术。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。