第230集防止超卖架构实战:高并发库存控制、分布式锁、秒杀系统的企业级解决方案

前言

在当今电商、秒杀、抢购等高并发业务场景中,防止超卖已成为系统架构的核心挑战。超卖问题不仅会造成经济损失,还会严重影响用户体验和品牌信誉。传统的库存控制方式在高并发场景下往往无法有效防止超卖,需要采用分布式锁、乐观锁、消息队列等多种技术手段来构建可靠的防超卖机制。随着业务规模的不断扩大和并发量的持续增长,构建可扩展、高可靠的防超卖系统,已成为企业级架构师必须掌握的关键技能。

本文将深入探讨防止超卖的架构设计与实战应用,从高并发库存控制到分布式锁机制,从秒杀系统到业务保护策略,为企业构建稳定、可靠的防超卖解决方案提供全面的技术指导。

一、防止超卖架构概述与核心原理

1.1 防止超卖架构设计

防止超卖系统采用多层防护架构设计,通过库存预扣、分布式锁、消息队列等技术,实现可靠的库存控制能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
graph TB
A[用户请求] --> B[限流层]
B --> C[库存预扣]
C --> D[分布式锁]
D --> E[库存检查]
E --> F[库存扣减]
F --> G[订单创建]
G --> H[支付处理]
H --> I[库存确认]

J[防护策略] --> K[乐观锁]
J --> L[悲观锁]
J --> M[分布式锁]
J --> N[消息队列]

O[库存管理] --> P[Redis缓存]
O --> Q[数据库存储]
O --> R[库存同步]
O --> S[库存回滚]

T[监控告警] --> U[库存监控]
T --> V[超卖检测]
T --> W[异常告警]
T --> X[性能监控]

1.2 防止超卖核心特性

1.2.1 多层防护机制

  • 限流层:通过限流控制并发请求量
  • 预扣层:提前扣减库存避免超卖
  • 锁机制:使用分布式锁保证原子性
  • 确认层:支付成功后确认库存扣减

1.2.2 库存控制策略

  • 乐观锁:基于版本号的乐观并发控制
  • 悲观锁:基于数据库锁的悲观并发控制
  • 分布式锁:基于Redis的分布式锁机制
  • 消息队列:异步处理库存扣减操作

1.2.3 业务保护机制

  • 库存回滚:订单取消时回滚库存
  • 超时处理:预扣库存超时自动释放
  • 异常处理:系统异常时的库存保护
  • 监控告警:实时监控库存状态

二、防止超卖核心实现

2.1 库存管理服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// 库存管理服务
@Service
@Slf4j
public class InventoryService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private InventoryMapper inventoryMapper;

@Autowired
private DistributedLockService lockService;

@Autowired
private MessageQueueService messageQueueService;

private static final String INVENTORY_KEY_PREFIX = "inventory:";
private static final String LOCK_KEY_PREFIX = "lock:inventory:";
private static final String PRE_DEDUCT_KEY_PREFIX = "pre_deduct:";

/**
* 预扣库存
*/
public PreDeductResult preDeductInventory(String productId, int quantity, String orderId) {
String lockKey = LOCK_KEY_PREFIX + productId;
String inventoryKey = INVENTORY_KEY_PREFIX + productId;
String preDeductKey = PRE_DEDUCT_KEY_PREFIX + orderId;

// 1. 获取分布式锁
String lockValue = UUID.randomUUID().toString();
boolean lockAcquired = lockService.tryLock(lockKey, lockValue, 30, TimeUnit.SECONDS);

if (!lockAcquired) {
return PreDeductResult.failed("获取锁失败");
}

try {
// 2. 检查库存
Integer currentStock = getCurrentStock(productId);
if (currentStock == null || currentStock < quantity) {
return PreDeductResult.failed("库存不足");
}

// 3. 预扣库存
boolean deductSuccess = deductStock(productId, quantity);
if (!deductSuccess) {
return PreDeductResult.failed("库存扣减失败");
}

// 4. 记录预扣信息
PreDeductInfo preDeductInfo = new PreDeductInfo();
preDeductInfo.setProductId(productId);
preDeductInfo.setQuantity(quantity);
preDeductInfo.setOrderId(orderId);
preDeductInfo.setPreDeductTime(System.currentTimeMillis());
preDeductInfo.setExpireTime(System.currentTimeMillis() + 30 * 60 * 1000); // 30分钟过期

redisTemplate.opsForValue().set(preDeductKey, preDeductInfo, 30, TimeUnit.MINUTES);

// 5. 发送预扣消息
messageQueueService.sendPreDeductMessage(preDeductInfo);

return PreDeductResult.success(preDeductInfo);

} finally {
// 6. 释放锁
lockService.releaseLock(lockKey, lockValue);
}
}

/**
* 确认库存扣减
*/
public boolean confirmInventoryDeduct(String orderId) {
String preDeductKey = PRE_DEDUCT_KEY_PREFIX + orderId;

// 1. 获取预扣信息
PreDeductInfo preDeductInfo = (PreDeductInfo) redisTemplate.opsForValue().get(preDeductKey);
if (preDeductInfo == null) {
log.warn("预扣信息不存在: orderId={}", orderId);
return false;
}

// 2. 确认库存扣减
boolean confirmSuccess = confirmStockDeduct(preDeductInfo.getProductId(),
preDeductInfo.getQuantity());

if (confirmSuccess) {
// 3. 删除预扣信息
redisTemplate.delete(preDeductKey);

// 4. 发送确认消息
messageQueueService.sendConfirmMessage(preDeductInfo);

log.info("库存扣减确认成功: orderId={}, productId={}, quantity={}",
orderId, preDeductInfo.getProductId(), preDeductInfo.getQuantity());
}

return confirmSuccess;
}

/**
* 回滚库存
*/
public boolean rollbackInventory(String orderId) {
String preDeductKey = PRE_DEDUCT_KEY_PREFIX + orderId;

// 1. 获取预扣信息
PreDeductInfo preDeductInfo = (PreDeductInfo) redisTemplate.opsForValue().get(preDeductKey);
if (preDeductInfo == null) {
log.warn("预扣信息不存在: orderId={}", orderId);
return false;
}

// 2. 回滚库存
boolean rollbackSuccess = rollbackStock(preDeductInfo.getProductId(),
preDeductInfo.getQuantity());

if (rollbackSuccess) {
// 3. 删除预扣信息
redisTemplate.delete(preDeductKey);

// 4. 发送回滚消息
messageQueueService.sendRollbackMessage(preDeductInfo);

log.info("库存回滚成功: orderId={}, productId={}, quantity={}",
orderId, preDeductInfo.getProductId(), preDeductInfo.getQuantity());
}

return rollbackSuccess;
}

/**
* 获取当前库存
*/
private Integer getCurrentStock(String productId) {
String inventoryKey = INVENTORY_KEY_PREFIX + productId;

// 1. 从Redis获取
Integer redisStock = (Integer) redisTemplate.opsForValue().get(inventoryKey);
if (redisStock != null) {
return redisStock;
}

// 2. 从数据库获取
Inventory inventory = inventoryMapper.selectByProductId(productId);
if (inventory != null) {
// 3. 更新Redis缓存
redisTemplate.opsForValue().set(inventoryKey, inventory.getStock(), 1, TimeUnit.HOURS);
return inventory.getStock();
}

return null;
}

/**
* 扣减库存
*/
private boolean deductStock(String productId, int quantity) {
String inventoryKey = INVENTORY_KEY_PREFIX + productId;

// 1. Redis扣减
Long result = redisTemplate.opsForValue().increment(inventoryKey, -quantity);
if (result < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(inventoryKey, quantity);
return false;
}

// 2. 数据库扣减
int updateCount = inventoryMapper.deductStock(productId, quantity);
if (updateCount == 0) {
// 数据库扣减失败,回滚Redis
redisTemplate.opsForValue().increment(inventoryKey, quantity);
return false;
}

return true;
}

/**
* 确认库存扣减
*/
private boolean confirmStockDeduct(String productId, int quantity) {
// 更新数据库状态
return inventoryMapper.confirmDeduct(productId, quantity) > 0;
}

/**
* 回滚库存
*/
private boolean rollbackStock(String productId, int quantity) {
String inventoryKey = INVENTORY_KEY_PREFIX + productId;

// 1. Redis回滚
redisTemplate.opsForValue().increment(inventoryKey, quantity);

// 2. 数据库回滚
return inventoryMapper.rollbackStock(productId, quantity) > 0;
}
}

// 预扣结果
public class PreDeductResult {
private boolean success;
private String message;
private PreDeductInfo preDeductInfo;

public static PreDeductResult success(PreDeductInfo preDeductInfo) {
PreDeductResult result = new PreDeductResult();
result.success = true;
result.preDeductInfo = preDeductInfo;
return result;
}

public static PreDeductResult failed(String message) {
PreDeductResult result = new PreDeductResult();
result.success = false;
result.message = message;
return result;
}

// getter/setter方法
}

// 预扣信息
public class PreDeductInfo {
private String productId;
private int quantity;
private String orderId;
private long preDeductTime;
private long expireTime;

// 构造函数和getter/setter方法
}

2.2 分布式锁服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// 分布式锁服务
@Service
@Slf4j
public class DistributedLockService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private static final String LOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";

private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";

/**
* 尝试获取锁
*/
public boolean tryLock(String lockKey, String lockValue, long expireTime, TimeUnit timeUnit) {
try {
Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue,
expireTime, timeUnit);
return success != null && success;
} catch (Exception e) {
log.error("获取锁失败: lockKey={}", lockKey, e);
return false;
}
}

/**
* 获取锁(阻塞)
*/
public boolean lock(String lockKey, String lockValue, long expireTime, TimeUnit timeUnit) {
return lock(lockKey, lockValue, expireTime, timeUnit, 30, TimeUnit.SECONDS);
}

/**
* 获取锁(带超时)
*/
public boolean lock(String lockKey, String lockValue, long expireTime, TimeUnit timeUnit,
long waitTime, TimeUnit waitTimeUnit) {
long startTime = System.currentTimeMillis();
long waitTimeMillis = waitTimeUnit.toMillis(waitTime);

while (System.currentTimeMillis() - startTime < waitTimeMillis) {
if (tryLock(lockKey, lockValue, expireTime, timeUnit)) {
return true;
}

try {
Thread.sleep(100); // 等待100ms后重试
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}

return false;
}

/**
* 释放锁
*/
public boolean releaseLock(String lockKey, String lockValue) {
try {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(UNLOCK_SCRIPT);
script.setResultType(Long.class);

Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue);
return result != null && result == 1;
} catch (Exception e) {
log.error("释放锁失败: lockKey={}", lockKey, e);
return false;
}
}

/**
* 续期锁
*/
public boolean renewLock(String lockKey, String lockValue, long expireTime, TimeUnit timeUnit) {
try {
String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
if (lockValue.equals(currentValue)) {
redisTemplate.expire(lockKey, expireTime, timeUnit);
return true;
}
return false;
} catch (Exception e) {
log.error("续期锁失败: lockKey={}", lockKey, e);
return false;
}
}
}

// 可重入分布式锁
@Component
@Slf4j
public class ReentrantDistributedLock {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final ThreadLocal<Map<String, Integer>> lockCount = new ThreadLocal<>();

/**
* 获取可重入锁
*/
public boolean lock(String lockKey, String lockValue, long expireTime, TimeUnit timeUnit) {
Map<String, Integer> countMap = lockCount.get();
if (countMap == null) {
countMap = new HashMap<>();
lockCount.set(countMap);
}

Integer count = countMap.get(lockKey);
if (count != null && count > 0) {
// 重入锁
countMap.put(lockKey, count + 1);
return true;
}

// 尝试获取锁
Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue,
expireTime, timeUnit);

if (success != null && success) {
countMap.put(lockKey, 1);
return true;
}

return false;
}

/**
* 释放可重入锁
*/
public boolean unlock(String lockKey, String lockValue) {
Map<String, Integer> countMap = lockCount.get();
if (countMap == null) {
return false;
}

Integer count = countMap.get(lockKey);
if (count == null || count <= 0) {
return false;
}

if (count > 1) {
// 重入锁,减少计数
countMap.put(lockKey, count - 1);
return true;
}

// 释放锁
String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
if (lockValue.equals(currentValue)) {
redisTemplate.delete(lockKey);
countMap.remove(lockKey);
return true;
}

return false;
}
}

2.3 乐观锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// 乐观锁库存管理
@Service
@Slf4j
public class OptimisticLockInventoryService {

@Autowired
private InventoryMapper inventoryMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 乐观锁扣减库存
*/
public boolean deductStockWithOptimisticLock(String productId, int quantity, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
try {
// 1. 获取当前库存信息
Inventory inventory = inventoryMapper.selectByProductId(productId);
if (inventory == null) {
log.warn("商品不存在: productId={}", productId);
return false;
}

// 2. 检查库存是否充足
if (inventory.getStock() < quantity) {
log.warn("库存不足: productId={}, currentStock={}, requiredQuantity={}",
productId, inventory.getStock(), quantity);
return false;
}

// 3. 乐观锁更新库存
int updateCount = inventoryMapper.updateStockWithVersion(
productId, quantity, inventory.getVersion());

if (updateCount > 0) {
// 4. 更新Redis缓存
updateRedisCache(productId, inventory.getStock() - quantity);

log.info("乐观锁扣减库存成功: productId={}, quantity={}, version={}",
productId, quantity, inventory.getVersion());
return true;
}

// 5. 更新失败,等待后重试
Thread.sleep(50 + i * 10); // 递增等待时间

} catch (Exception e) {
log.error("乐观锁扣减库存异常: productId={}, retry={}", productId, i, e);
if (i == maxRetries - 1) {
throw new RuntimeException("乐观锁扣减库存失败", e);
}
}
}

return false;
}

/**
* 批量乐观锁扣减库存
*/
public Map<String, Boolean> batchDeductStockWithOptimisticLock(
Map<String, Integer> productQuantities, int maxRetries) {

Map<String, Boolean> results = new HashMap<>();

for (Map.Entry<String, Integer> entry : productQuantities.entrySet()) {
String productId = entry.getKey();
Integer quantity = entry.getValue();

boolean success = deductStockWithOptimisticLock(productId, quantity, maxRetries);
results.put(productId, success);
}

return results;
}

/**
* 更新Redis缓存
*/
private void updateRedisCache(String productId, int newStock) {
String inventoryKey = "inventory:" + productId;
redisTemplate.opsForValue().set(inventoryKey, newStock, 1, TimeUnit.HOURS);
}
}

// 库存实体(带版本号)
public class Inventory {
private String productId;
private String productName;
private Integer stock;
private Integer version;
private Date createTime;
private Date updateTime;

// 构造函数和getter/setter方法
}

// 库存Mapper
@Mapper
public interface InventoryMapper {

/**
* 根据商品ID查询库存
*/
@Select("SELECT * FROM inventory WHERE product_id = #{productId}")
Inventory selectByProductId(@Param("productId") String productId);

/**
* 乐观锁更新库存
*/
@Update("UPDATE inventory SET stock = stock - #{quantity}, version = version + 1, " +
"update_time = NOW() WHERE product_id = #{productId} AND version = #{version}")
int updateStockWithVersion(@Param("productId") String productId,
@Param("quantity") int quantity,
@Param("version") int version);

/**
* 扣减库存
*/
@Update("UPDATE inventory SET stock = stock - #{quantity}, update_time = NOW() " +
"WHERE product_id = #{productId} AND stock >= #{quantity}")
int deductStock(@Param("productId") String productId, @Param("quantity") int quantity);

/**
* 确认扣减
*/
@Update("UPDATE inventory SET status = 'CONFIRMED', update_time = NOW() " +
"WHERE product_id = #{productId}")
int confirmDeduct(@Param("productId") String productId, @Param("quantity") int quantity);

/**
* 回滚库存
*/
@Update("UPDATE inventory SET stock = stock + #{quantity}, update_time = NOW() " +
"WHERE product_id = #{productId}")
int rollbackStock(@Param("productId") String productId, @Param("quantity") int quantity);
}

2.4 消息队列处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// 消息队列服务
@Service
@Slf4j
public class MessageQueueService {

@Autowired
private RabbitTemplate rabbitTemplate;

/**
* 发送预扣消息
*/
public void sendPreDeductMessage(PreDeductInfo preDeductInfo) {
try {
PreDeductMessage message = new PreDeductMessage();
message.setProductId(preDeductInfo.getProductId());
message.setQuantity(preDeductInfo.getQuantity());
message.setOrderId(preDeductInfo.getOrderId());
message.setPreDeductTime(preDeductInfo.getPreDeductTime());
message.setExpireTime(preDeductInfo.getExpireTime());

rabbitTemplate.convertAndSend("inventory.exchange", "pre.deduct", message);

log.info("发送预扣消息成功: orderId={}", preDeductInfo.getOrderId());

} catch (Exception e) {
log.error("发送预扣消息失败: orderId={}", preDeductInfo.getOrderId(), e);
}
}

/**
* 发送确认消息
*/
public void sendConfirmMessage(PreDeductInfo preDeductInfo) {
try {
ConfirmMessage message = new ConfirmMessage();
message.setProductId(preDeductInfo.getProductId());
message.setQuantity(preDeductInfo.getQuantity());
message.setOrderId(preDeductInfo.getOrderId());
message.setConfirmTime(System.currentTimeMillis());

rabbitTemplate.convertAndSend("inventory.exchange", "confirm", message);

log.info("发送确认消息成功: orderId={}", preDeductInfo.getOrderId());

} catch (Exception e) {
log.error("发送确认消息失败: orderId={}", preDeductInfo.getOrderId(), e);
}
}

/**
* 发送回滚消息
*/
public void sendRollbackMessage(PreDeductInfo preDeductInfo) {
try {
RollbackMessage message = new RollbackMessage();
message.setProductId(preDeductInfo.getProductId());
message.setQuantity(preDeductInfo.getQuantity());
message.setOrderId(preDeductInfo.getOrderId());
message.setRollbackTime(System.currentTimeMillis());

rabbitTemplate.convertAndSend("inventory.exchange", "rollback", message);

log.info("发送回滚消息成功: orderId={}", preDeductInfo.getOrderId());

} catch (Exception e) {
log.error("发送回滚消息失败: orderId={}", preDeductInfo.getOrderId(), e);
}
}
}

// 预扣消息监听器
@Component
@Slf4j
public class PreDeductMessageListener {

@Autowired
private InventoryService inventoryService;

@RabbitListener(queues = "pre.deduct.queue")
public void handlePreDeductMessage(PreDeductMessage message) {
try {
log.info("处理预扣消息: orderId={}, productId={}, quantity={}",
message.getOrderId(), message.getProductId(), message.getQuantity());

// 处理预扣逻辑
// 这里可以添加额外的业务逻辑,如记录日志、发送通知等

} catch (Exception e) {
log.error("处理预扣消息失败: orderId={}", message.getOrderId(), e);
}
}
}

// 确认消息监听器
@Component
@Slf4j
public class ConfirmMessageListener {

@Autowired
private OrderService orderService;

@RabbitListener(queues = "confirm.queue")
public void handleConfirmMessage(ConfirmMessage message) {
try {
log.info("处理确认消息: orderId={}, productId={}, quantity={}",
message.getOrderId(), message.getProductId(), message.getQuantity());

// 更新订单状态
orderService.updateOrderStatus(message.getOrderId(), "CONFIRMED");

} catch (Exception e) {
log.error("处理确认消息失败: orderId={}", message.getOrderId(), e);
}
}
}

// 回滚消息监听器
@Component
@Slf4j
public class RollbackMessageListener {

@Autowired
private OrderService orderService;

@RabbitListener(queues = "rollback.queue")
public void handleRollbackMessage(RollbackMessage message) {
try {
log.info("处理回滚消息: orderId={}, productId={}, quantity={}",
message.getOrderId(), message.getProductId(), message.getQuantity());

// 更新订单状态
orderService.updateOrderStatus(message.getOrderId(), "CANCELLED");

} catch (Exception e) {
log.error("处理回滚消息失败: orderId={}", message.getOrderId(), e);
}
}
}

// 消息实体类
public class PreDeductMessage {
private String productId;
private int quantity;
private String orderId;
private long preDeductTime;
private long expireTime;

// 构造函数和getter/setter方法
}

public class ConfirmMessage {
private String productId;
private int quantity;
private String orderId;
private long confirmTime;

// 构造函数和getter/setter方法
}

public class RollbackMessage {
private String productId;
private int quantity;
private String orderId;
private long rollbackTime;

// 构造函数和getter/setter方法
}

三、秒杀系统防超卖实现

3.1 秒杀控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// 秒杀控制器
@RestController
@RequestMapping("/seckill")
@Slf4j
public class SeckillController {

@Autowired
private SeckillService seckillService;

@Autowired
private RateLimiterService rateLimiterService;

/**
* 秒杀接口
*/
@PostMapping("/{productId}")
public ResponseEntity<SeckillResult> seckill(@PathVariable String productId,
@RequestParam int quantity,
@RequestHeader("userId") String userId) {
try {
// 1. 限流检查
if (!rateLimiterService.tryAcquire(userId)) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.body(SeckillResult.failed("请求过于频繁,请稍后再试"));
}

// 2. 执行秒杀
SeckillResult result = seckillService.executeSeckill(productId, quantity, userId);

if (result.isSuccess()) {
return ResponseEntity.ok(result);
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
}

} catch (Exception e) {
log.error("秒杀执行失败: productId={}, userId={}", productId, userId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(SeckillResult.failed("系统异常,请稍后再试"));
}
}

/**
* 查询秒杀结果
*/
@GetMapping("/result/{orderId}")
public ResponseEntity<SeckillResult> getSeckillResult(@PathVariable String orderId) {
try {
SeckillResult result = seckillService.getSeckillResult(orderId);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("查询秒杀结果失败: orderId={}", orderId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}

// 秒杀服务
@Service
@Slf4j
public class SeckillService {

@Autowired
private InventoryService inventoryService;

@Autowired
private OrderService orderService;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private static final String SECKILL_RESULT_KEY_PREFIX = "seckill:result:";

/**
* 执行秒杀
*/
public SeckillResult executeSeckill(String productId, int quantity, String userId) {
String orderId = generateOrderId();

try {
// 1. 预扣库存
PreDeductResult preDeductResult = inventoryService.preDeductInventory(
productId, quantity, orderId);

if (!preDeductResult.isSuccess()) {
return SeckillResult.failed(preDeductResult.getMessage());
}

// 2. 创建订单
Order order = createOrder(orderId, productId, quantity, userId);
if (order == null) {
// 创建订单失败,回滚库存
inventoryService.rollbackInventory(orderId);
return SeckillResult.failed("创建订单失败");
}

// 3. 缓存秒杀结果
SeckillResult result = SeckillResult.success(orderId, "秒杀成功,请尽快支付");
cacheSeckillResult(orderId, result);

// 4. 异步处理订单
processOrderAsync(order);

return result;

} catch (Exception e) {
log.error("秒杀执行异常: productId={}, userId={}", productId, userId, e);

// 异常情况下回滚库存
inventoryService.rollbackInventory(orderId);

return SeckillResult.failed("系统异常,请稍后再试");
}
}

/**
* 获取秒杀结果
*/
public SeckillResult getSeckillResult(String orderId) {
String resultKey = SECKILL_RESULT_KEY_PREFIX + orderId;
SeckillResult result = (SeckillResult) redisTemplate.opsForValue().get(resultKey);

if (result == null) {
// 从数据库查询
Order order = orderService.getOrderById(orderId);
if (order != null) {
result = SeckillResult.success(orderId, "订单已创建");
} else {
result = SeckillResult.failed("订单不存在");
}
}

return result;
}

/**
* 创建订单
*/
private Order createOrder(String orderId, String productId, int quantity, String userId) {
try {
Order order = new Order();
order.setOrderId(orderId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setUserId(userId);
order.setStatus("PENDING");
order.setCreateTime(new Date());

return orderService.createOrder(order);
} catch (Exception e) {
log.error("创建订单失败: orderId={}", orderId, e);
return null;
}
}

/**
* 缓存秒杀结果
*/
private void cacheSeckillResult(String orderId, SeckillResult result) {
String resultKey = SECKILL_RESULT_KEY_PREFIX + orderId;
redisTemplate.opsForValue().set(resultKey, result, 30, TimeUnit.MINUTES);
}

/**
* 异步处理订单
*/
@Async
public void processOrderAsync(Order order) {
try {
// 模拟订单处理逻辑
Thread.sleep(1000);

// 更新订单状态
orderService.updateOrderStatus(order.getOrderId(), "PROCESSING");

} catch (Exception e) {
log.error("异步处理订单失败: orderId={}", order.getOrderId(), e);
}
}

/**
* 生成订单ID
*/
private String generateOrderId() {
return "SK" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 9999);
}
}

// 秒杀结果
public class SeckillResult {
private boolean success;
private String message;
private String orderId;
private String status;

public static SeckillResult success(String orderId, String message) {
SeckillResult result = new SeckillResult();
result.success = true;
result.orderId = orderId;
result.message = message;
result.status = "SUCCESS";
return result;
}

public static SeckillResult failed(String message) {
SeckillResult result = new SeckillResult();
result.success = false;
result.message = message;
result.status = "FAILED";
return result;
}

// 构造函数和getter/setter方法
}

3.2 限流服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 限流服务
@Service
@Slf4j
public class RateLimiterService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private static final String RATE_LIMIT_KEY_PREFIX = "rate_limit:";
private static final int DEFAULT_LIMIT = 10; // 每秒10次
private static final int DEFAULT_WINDOW = 1; // 1秒窗口

/**
* 尝试获取令牌
*/
public boolean tryAcquire(String userId) {
return tryAcquire(userId, DEFAULT_LIMIT, DEFAULT_WINDOW);
}

/**
* 尝试获取令牌(自定义限制)
*/
public boolean tryAcquire(String userId, int limit, int windowSeconds) {
String key = RATE_LIMIT_KEY_PREFIX + userId;

try {
// 使用滑动窗口算法
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - windowSeconds * 1000;

// 清理过期数据
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);

// 获取当前窗口内的请求数
Long currentCount = redisTemplate.opsForZSet().count(key, windowStart, currentTime);

if (currentCount != null && currentCount >= limit) {
return false; // 超过限制
}

// 添加当前请求
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), currentTime);
redisTemplate.expire(key, windowSeconds + 1, TimeUnit.SECONDS);

return true;

} catch (Exception e) {
log.error("限流检查失败: userId={}", userId, e);
return true; // 异常情况下允许通过
}
}

/**
* 获取剩余令牌数
*/
public int getRemainingTokens(String userId) {
String key = RATE_LIMIT_KEY_PREFIX + userId;

try {
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - DEFAULT_WINDOW * 1000;

Long currentCount = redisTemplate.opsForZSet().count(key, windowStart, currentTime);
return Math.max(0, DEFAULT_LIMIT - (currentCount != null ? currentCount.intValue() : 0));

} catch (Exception e) {
log.error("获取剩余令牌失败: userId={}", userId, e);
return DEFAULT_LIMIT;
}
}
}

四、最佳实践与总结

4.1 防止超卖最佳实践

4.1.1 多层防护策略

  • 限流层:通过限流控制并发请求量
  • 预扣层:提前扣减库存避免超卖
  • 锁机制:使用分布式锁保证原子性
  • 确认层:支付成功后确认库存扣减

4.1.2 库存控制策略

  • 乐观锁:适合读多写少的场景
  • 悲观锁:适合写多读少的场景
  • 分布式锁:适合分布式环境
  • 消息队列:适合异步处理场景

4.1.3 异常处理策略

  • 库存回滚:订单取消时回滚库存
  • 超时处理:预扣库存超时自动释放
  • 异常恢复:系统异常时的库存保护
  • 监控告警:实时监控库存状态

4.1.4 性能优化策略

  • 缓存优化:使用Redis缓存库存信息
  • 批量处理:批量处理库存操作
  • 异步处理:异步处理非关键操作
  • 分库分表:水平拆分库存表

4.2 架构演进建议

4.2.1 微服务架构演进

  • 服务拆分:将库存服务拆分为独立微服务
  • 服务治理:实现服务的注册发现、负载均衡
  • 数据一致性:使用分布式事务保证数据一致性
  • 容错处理:实现熔断、降级、重试机制

4.2.2 云原生架构演进

  • 容器化部署:使用Docker等容器技术部署
  • 弹性伸缩:实现基于负载的自动扩缩容
  • 服务网格:使用Istio等服务网格技术
  • 云原生存储:使用云原生的存储服务

4.2.3 智能化运维

  • AI驱动优化:使用机器学习优化库存策略
  • 预测性分析:预测库存需求和超卖风险
  • 自动调优:实现基于监控数据的自动调优
  • 智能告警:实现智能告警和故障诊断

4.3 总结

防止超卖是企业级电商系统的核心挑战,需要采用多层防护、多种技术手段来构建可靠的防超卖机制。通过合理的架构设计,完善的锁机制,可靠的库存控制,可以实现稳定、高效的防超卖解决方案。随着业务规模的不断扩大和并发量的持续增长,防超卖系统将更加智能化和自动化。

在未来的发展中,企业需要持续关注技术发展趋势,不断优化和完善防超卖策略,以适应不断变化的业务需求和技术环境。通过本文的深入分析和实践指导,希望能够为企业构建高质量的防超卖解决方案提供有价值的参考和帮助,推动企业级应用在库存管理场景下的稳定运行和持续发展。