# 怎么用Java實現秒殺系統
## 1. 秒殺系統核心挑戰與技術難點
秒殺系統是典型的高并發場景,需要解決以下核心問題:
1. **瞬時高并發流量**:活動開始瞬間可能產生數十萬級QPS
2. **超賣問題**:庫存扣減的原子性保證
3. **惡意請求**:黃牛腳本、DDOS攻擊等
4. **系統雪崩**:避免單一服務崩潰導致整個系統不可用
5. **數據一致性**:庫存、訂單、支付的狀態同步
## 2. 系統架構設計
### 2.1 分層架構設計
┌───────────────────────────────────┐ │ 客戶端層 │ │ (靜態資源CDN、請求限流、人機驗證) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 接入層 │ │ (負載均衡、API網關、風控攔截) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 服務層 │ │ (業務邏輯、分布式事務、緩存) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 數據層 │ │ (分庫分表、讀寫分離、MQ異步) │ └───────────────────────────────────┘
### 2.2 技術選型建議
| 組件類型 | 推薦方案 |
|----------------|-----------------------------------|
| 開發框架 | Spring Boot + Spring Cloud |
| 緩存 | Redis Cluster + Redisson |
| 消息隊列 | RocketMQ/Kafka |
| 數據庫 | MySQL + ShardingSphere |
| 限流組件 | Sentinel/Guava RateLimiter |
| 分布式鎖 | Redis Lua腳本/Zookeeper |
| 監控系統 | Prometheus + Grafana |
## 3. 核心代碼實現
### 3.1 庫存預熱與緩存
```java
@Service
public class InventoryService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 商品庫存預熱
public void preheatInventory(Long itemId, int stock) {
String key = "sec_kill:stock:" + itemId;
redisTemplate.opsForValue().set(key, String.valueOf(stock));
// 設置過期時間避免臟數據
redisTemplate.expire(key, 24, TimeUnit.HOURS);
}
// 獲取當前緩存庫存
public int getCacheStock(Long itemId) {
String key = "sec_kill:stock:" + itemId;
String val = redisTemplate.opsForValue().get(key);
return val == null ? 0 : Integer.parseInt(val);
}
}
public class RedisDistributedLock {
private static final String LOCK_PREFIX = "sec_kill:lock:";
private static final int DEFAULT_EXPIRE = 30; // 秒
public static boolean tryLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
String result = commands.set(key, requestId,
"NX", "EX", DEFAULT_EXPIRE);
return "OK".equals(result);
});
}
public static boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
return redisTemplate.execute(
(RedisCallback<Boolean>) connection -> {
Object result = connection.eval(
script.getBytes(),
ReturnType.INTEGER,
1,
(LOCK_PREFIX + lockKey).getBytes(),
requestId.getBytes());
return result.equals(1L);
});
}
}
@RestController
public class SecKillController {
@PostMapping("/seckill")
public Result seckill(@RequestParam Long itemId,
@RequestParam Integer userId) {
// 1. 參數校驗
if (itemId == null || userId == null) {
return Result.error("參數錯誤");
}
// 2. 風控校驗(頻率限制、黑名單等)
if (!riskCheck(userId)) {
return Result.error("操作頻繁");
}
// 3. 內存標記(減少Redis訪問)
if (!ItemStockCache.getStatus(itemId)) {
return Result.error("已售罄");
}
// 4. Redis預減庫存
Long stock = redisTemplate.opsForValue().decrement(
"sec_kill:stock:" + itemId);
if (stock == null || stock < 0) {
// 恢復庫存
redisTemplate.opsForValue().increment(
"sec_kill:stock:" + itemId);
ItemStockCache.setStatus(itemId, false);
return Result.error("已售罄");
}
// 5. 消息隊列異步處理
SecKillMessage message = new SecKillMessage(userId, itemId);
rocketMQTemplate.send("sec_kill_queue", message);
return Result.success("排隊中");
}
}
@Component
@RocketMQMessageListener(
topic = "sec_kill_queue",
consumerGroup = "sec_kill_group")
public class OrderConsumer implements RocketMQListener<SecKillMessage> {
@Override
public void onMessage(SecKillMessage message) {
try {
// 1. 數據庫校驗庫存
Item item = itemMapper.selectForUpdate(message.getItemId());
if (item.getStock() <= 0) {
return;
}
// 2. 創建訂單
Order order = createOrder(message.getUserId(), item);
// 3. 扣減真實庫存
int affected = itemMapper.reduceStock(
message.getItemId(), order.getCount());
if (affected == 0) {
throw new RuntimeException("庫存不足");
}
// 4. 支付超時處理(定時任務)
delayQueue.add(new PayCheckTask(order.getId()));
} catch (Exception e) {
// 恢復Redis庫存
redisTemplate.opsForValue().increment(
"sec_kill:stock:" + message.getItemId());
ItemStockCache.setStatus(message.getItemId(), true);
}
}
}
// 多級緩存示例
public class ItemCache {
@Autowired
private RedisTemplate redisTemplate;
private Cache<Long, Item> localCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
public Item getItem(Long id) {
// 1. 查本地緩存
Item item = localCache.getIfPresent(id);
if (item != null) return item;
// 2. 查Redis
String key = "item:" + id;
item = (Item)redisTemplate.opsForValue().get(key);
if (item != null) {
localCache.put(id, item);
return item;
}
// 3. 查數據庫
item = itemMapper.selectById(id);
if (item != null) {
redisTemplate.opsForValue().set(key, item, 1, TimeUnit.HOURS);
localCache.put(id, item);
}
return item;
}
}
// 令牌桶限流實現
public class RateLimiterService {
private final RateLimiter rateLimiter = RateLimiter.create(1000); // QPS=1000
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
}
// 分桶庫存示例
public class BucketStockService {
public boolean reduceStock(Long itemId) {
// 將庫存分為10個桶
String bucketKey = "stock_bucket:" + itemId + ":" +
(ThreadLocalRandom.current().nextInt(10));
Long remain = redisTemplate.execute(
new DefaultRedisScript<>(STOCK_REDUCE_SCRIPT, Long.class),
Collections.singletonList(bucketKey));
return remain != null && remain >= 0;
}
private static final String STOCK_REDUCE_SCRIPT =
"local current = tonumber(redis.call('get', KEYS[1])) " +
"if current > 0 then " +
" redis.call('decr', KEYS[1]) " +
" return 1 " +
"else " +
" return -1 " +
"end";
}
// 滑動窗口限流
public class SlidingWindowLimiter {
private final RedisTemplate<String, String> redisTemplate;
public boolean allowRequest(String key, int windowSize, int maxCount) {
long now = System.currentTimeMillis();
long windowStart = now - windowSize * 1000L;
// 使用Redis ZSET實現滑動窗口
redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
Long count = redisTemplate.opsForZSet().zCard(key);
return count != null && count <= maxCount;
}
}
// 熔斷降級示例
@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
public class SecKillController {
@HystrixCommand(
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1000"),
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50")
},
fallbackMethod = "seckillFallback"
)
@PostMapping("/seckill")
public Result seckill(...) {
// 正常業務邏輯
}
public Result seckillFallback(...) {
return Result.error("系統繁忙,請稍后重試");
}
public Result defaultFallback() {
return Result.error("服務暫時不可用");
}
}
實現高性能秒殺系統需要綜合運用多種技術:
擴展方向: - 結合大數據分析用戶行為 - 實現動態庫存調整 - 搭建智能風控系統 - 探索Serverless架構應用
實際開發中需要根據業務特點進行調整,建議先在小流量場景驗證方案可行性,再逐步全量上線。 “`
這篇文章共計約3650字,涵蓋了秒殺系統的完整實現方案,從架構設計到核心代碼實現,再到優化策略和監控方案。如需進一步擴展某些模塊或調整技術方案,可以具體討論補充。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。