在电商领域,秒杀活动是最具挑战性的业务场景之一。当数万甚至百万用户在同一时刻涌入系统争抢有限商品时,系统面临四大核心挑战:
本文将深入探讨如何使用Java技术栈构建一个高性能、高可用的秒杀系统,涵盖从架构设计到具体实现的完整解决方案。
// Sentinel注解式限流
@SentinelResource(
value = "seckillFlow",
blockHandler = "handleFlowBlock"
)
public SeckillResponse seckill(SeckillRequest request) {
// 业务逻辑
}
public SeckillResponse handleFlowBlock(SeckillRequest request, BlockException ex) {
return new SeckillResponse(CODE_FLOW_LIMIT, "请求过于频繁");
}
// Redis+Lua分布式限流
String luaScript = "local current = redis.call('incr', KEYS[1])\n" +
"if current == 1 then\n" +
" redis.call('expire', KEYS[1], ARGV[1])\n" +
"end\n" +
"return current <= tonumber(ARGV[2])";
Boolean allowed = redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Boolean.class),
Collections.singletonList("rate_limit:" + userId),
"1", "100" // 1秒内最多100次请求
);
// Lua脚本保证原子性
String script =
"local stock = tonumber(redis.call('get', KEYS[1])) " +
"if stock > 0 then " +
" redis.call('decr', KEYS[1]) " +
" return 1 " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("stock:" + productId)
);
UPDATE product_stock
SET stock = stock - 1,
version = version + 1
WHERE product_id = #{productId}
AND stock > 0
public class SeckillMessage {
private String msgId; // 雪花算法ID
private Long userId;
private Long productId;
private String seckillToken;
private Long timestamp;
// 幂等控制字段
private String dedupKey;
}
public boolean createOrder(Order order) {
String lockKey = "order_lock:" + order.getUserId() + ":" + order.getProductId();
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
// 处理订单业务
return orderService.save(order);
}
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return false;
}
@Transactional
public void processOrder(SeckillMessage message) {
// 幂等检查
if (orderCache.containsKey(message.getDedupKey())) {
return;
}
// 业务处理
createOrder(message);
// 记录已处理
orderCache.put(message.getDedupKey(), true, 30, TimeUnit.MINUTES);
}
Caffeine
# JDK17推荐配置
-server
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=35
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=4
-- 订单分表逻辑
CREATE TABLE order_0000 ...
CREATE TABLE order_0001 ...
...
CREATE TABLE order_1023
-- 分表路由算法
table_suffix = order_id & 1023;
ALTER TABLE seckill_order
ADD INDEX idx_user_product(user_id, product_id),
ADD INDEX idx_create_time(create_time);
@SentinelResource(
value = "createOrderResource",
fallback = "createOrderFallback",
blockHandler = "createOrderBlockHandler",
exceptionsToIgnore = {IllegalArgumentException.class}
)
public OrderResult createOrder(OrderRequest request) {
// 业务逻辑
}
// 降级处理
public OrderResult createOrderFallback(OrderRequest request, Throwable ex) {
return new OrderResult(ERROR, "系统繁忙,请稍后重试");
}
指标类别 |
监控工具 |
预警阈值 |
系统QPS |
Prometheus |
> 15,000 |
订单延迟 |
Grafana |
P99 > 500ms |
Redis内存 |
Redis Exporter |
> 85% |
MySQL连接数 |
MySQl Exporter |
> 90% |
MQ消息堆积 |
RocketMQ Console |
> 50,000 |
@RocketMQMessageListener(topic = "seckill_order", consumerGroup = "order_consumer")
public class OrderConsumer implements RocketMQListener {
@Override
@Transactional(rollbackFor = Exception.class)
public void onMessage(SeckillMessage message) {
try {
orderService.createOrder(message);
} catch (Exception e) {
// 恢复Redis库存
redisTemplate.opsForValue().increment("stock:" + message.getProductId());
// 记录异常日志
log.error("订单创建失败: {}", message.getMsgId(), e);
}
}
}
apiVersion: apps/v1
kind: Deployment
metadata:
name: seckill-service
spec:
replicas: 20
strategy:
rollingUpdate:
maxSurge: 30%
maxUnavailable: 10%
template:
spec:
containers:
- name: seckill
image: registry.example.com/seckill:v2.0
resources:
limits:
cpu: "4"
memory: 8Gi
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: REDIS_CLUSTER
value: "redis-cluster:6379"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
架构师箴言:没有完美的架构,只有适合场景的架构。秒杀系统的核心在于用空间换时间,用可靠性换性能,在业务需求和系统资源之间找到最佳平衡点。