设计一个高并发下单系统

1. 概述

1.1 系统背景

高并发下单系统是电商平台的核心系统,需要处理:

  • 高并发请求:秒杀、大促等场景,QPS可达10万+
  • 数据一致性:库存扣减、订单创建必须准确
  • 高可用性:7×24小时稳定运行
  • 低延迟:用户下单体验流畅

1.2 核心挑战

技术挑战

  • 高并发:如何支撑百万级QPS
  • 数据一致性:如何保证库存和订单的一致性
  • 性能优化:如何降低延迟,提高吞吐量
  • 高可用:如何保障系统稳定运行

1.3 本文内容结构

本文将从以下几个方面全面解析高并发下单系统:

  1. 需求分析:功能需求、非功能需求
  2. 架构设计:整体架构、模块设计
  3. 高并发处理:限流、削峰、异步处理
  4. 数据一致性:分布式事务、最终一致性
  5. 性能优化:缓存、数据库优化、消息队列
  6. 高可用设计:容错、降级、监控
  7. 实战案例:完整实现方案

2. 需求分析

2.1 功能需求

2.1.1 核心功能

下单流程

  1. 商品查询:查询商品信息、库存
  2. 库存校验:校验库存是否充足
  3. 价格计算:计算商品价格、优惠、运费
  4. 订单创建:创建订单记录
  5. 库存扣减:扣减商品库存
  6. 支付处理:调用支付系统
  7. 订单通知:发送订单通知

2.1.2 扩展功能

订单管理

  • 订单查询
  • 订单取消
  • 订单退款
  • 订单状态更新

库存管理

  • 库存查询
  • 库存预警
  • 库存同步

2.2 非功能需求

2.2.1 性能需求

响应时间

  • 下单接口:< 200ms(P99)
  • 查询接口:< 100ms(P99)

吞吐量

  • 峰值QPS:10万+
  • 平均QPS:5万+

并发用户

  • 同时在线用户:100万+
  • 峰值并发:50万+

2.2.2 可用性需求

可用性指标

  • 系统可用性:99.99%(年停机时间< 52分钟)
  • 故障恢复时间:< 5分钟

容错能力

  • 单点故障不影响服务
  • 自动故障转移
  • 服务降级

2.2.3 数据一致性需求

强一致性

  • 库存扣减:必须准确
  • 订单创建:必须成功

最终一致性

  • 订单状态同步
  • 库存同步

3. 架构设计

3.1 整体架构

3.1.1 架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
用户请求

Nginx(负载均衡 + 限流)

API网关(路由 + 鉴权 + 限流)

├──→ 订单服务(订单处理)
├──→ 库存服务(库存管理)
├──→ 商品服务(商品信息)
├──→ 价格服务(价格计算)
└──→ 支付服务(支付处理)

├──→ Redis(缓存 + 分布式锁)
├──→ MySQL(数据持久化)
├──→ Kafka(消息队列)
└──→ Elasticsearch(订单搜索)

3.1.2 架构说明

接入层

  • Nginx:负载均衡、SSL终端
  • API网关:路由、鉴权、限流

服务层

  • 订单服务:订单创建、查询、管理
  • 库存服务:库存查询、扣减、回滚
  • 商品服务:商品信息查询
  • 价格服务:价格计算
  • 支付服务:支付处理

数据层

  • Redis:缓存、分布式锁、计数器
  • MySQL:数据持久化
  • Kafka:异步消息处理
  • Elasticsearch:订单搜索

3.2 模块设计

3.2.1 订单服务

职责

  • 订单创建
  • 订单查询
  • 订单状态管理
  • 订单取消

技术选型

  • Spring Boot
  • MySQL(主从)
  • Redis(缓存)

3.2.2 库存服务

职责

  • 库存查询
  • 库存扣减
  • 库存回滚
  • 库存预警

技术选型

  • Spring Boot
  • Redis(库存缓存)
  • MySQL(库存持久化)

3.2.3 商品服务

职责

  • 商品信息查询
  • 商品详情
  • 商品列表

技术选型

  • Spring Boot
  • Redis(商品缓存)
  • MySQL(商品数据)

4. 高并发处理

4.1 限流

4.1.1 限流策略

多级限流

  1. Nginx限流:IP级别限流
  2. API网关限流:接口级别限流
  3. 服务限流:服务级别限流

限流算法

  • 令牌桶:平滑限流
  • 漏桶:固定速率
  • 滑动窗口:时间窗口限流

4.1.2 Nginx限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# /etc/nginx/conf.d/limit.conf

# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=order_limit:10m rate=5r/s;

server {
listen 80;
server_name api.example.com;

# 下单接口限流(每秒5次)
location /api/order/create {
limit_req zone=order_limit burst=10 nodelay;
proxy_pass http://order_backend;
}

# 其他接口限流(每秒10次)
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://api_backend;
}
}

4.1.3 服务限流

Guava RateLimiter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class OrderService {

// 每秒100个请求
private final RateLimiter rateLimiter = RateLimiter.create(100.0);

public Order createOrder(OrderRequest request) {
// 限流
if (!rateLimiter.tryAcquire()) {
throw new BusinessException("请求过于频繁,请稍后再试");
}

// 处理订单
return doCreateOrder(request);
}
}

Redis限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class OrderService {

@Autowired
private RedisTemplate<String, String> redisTemplate;

public boolean checkRateLimit(String userId) {
String key = "rate:limit:order:" + userId;
Long count = redisTemplate.opsForValue().increment(key);

if (count == 1) {
redisTemplate.expire(key, 60, TimeUnit.SECONDS);
}

return count <= 10; // 每分钟最多10次
}
}

4.2 削峰

4.2.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
25
26
27
28
29
@Service
public class OrderService {

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

public void createOrderAsync(OrderRequest request) {
// 快速响应
String orderId = generateOrderId();

// 发送到消息队列
kafkaTemplate.send("order-create", orderId, JSON.toJSONString(request));

// 返回订单ID
return orderId;
}
}

@Component
public class OrderConsumer {

@KafkaListener(topics = "order-create", groupId = "order-processor")
public void processOrder(String orderId, String message) {
OrderRequest request = JSON.parseObject(message, OrderRequest.class);

// 异步处理订单
orderService.doCreateOrder(orderId, request);
}
}

4.2.2 队列削峰

Redis队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class OrderService {

@Autowired
private RedisTemplate<String, String> redisTemplate;

public void createOrderAsync(OrderRequest request) {
// 加入队列
redisTemplate.opsForList().rightPush("order:queue", JSON.toJSONString(request));
}
}

@Component
public class OrderQueueProcessor {

@Scheduled(fixedDelay = 100) // 每100ms处理一次
public void processOrderQueue() {
String message = redisTemplate.opsForList().leftPop("order:queue");
if (message != null) {
OrderRequest request = JSON.parseObject(message, OrderRequest.class);
orderService.doCreateOrder(request);
}
}
}

4.3 异步处理

4.3.1 下单异步化

同步流程

1
下单 → 库存扣减 → 订单创建 → 支付 → 返回

异步流程

1
下单 → 快速返回 → 异步处理(库存扣减、订单创建、支付)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class OrderService {

@Async
public CompletableFuture<Order> createOrderAsync(OrderRequest request) {
// 1. 库存扣减
inventoryService.deductStock(request.getSkuId(), request.getQuantity());

// 2. 订单创建
Order order = createOrder(request);

// 3. 支付处理
paymentService.processPayment(order);

return CompletableFuture.completedFuture(order);
}
}

5. 数据一致性

5.1 库存扣减

5.1.1 问题分析

库存扣减问题

  • 高并发下库存超卖
  • 库存扣减不一致
  • 库存回滚困难

5.1.2 解决方案

方案1:Redis分布式锁

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
@Service
public class InventoryService {

@Autowired
private RedisTemplate<String, String> redisTemplate;

public boolean deductStock(String skuId, int quantity) {
String lockKey = "lock:inventory:" + skuId;
String lockValue = UUID.randomUUID().toString();

try {
// 获取分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);

if (!locked) {
throw new BusinessException("系统繁忙,请稍后再试");
}

// 查询库存
String stockKey = "inventory:" + skuId;
Integer stock = Integer.parseInt(redisTemplate.opsForValue().get(stockKey));

if (stock < quantity) {
throw new BusinessException("库存不足");
}

// 扣减库存
Long newStock = redisTemplate.opsForValue().decrement(stockKey, quantity);

// 异步同步到数据库
syncToDatabase(skuId, quantity);

return true;
} finally {
// 释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey), lockValue);
}
}
}

方案2:Redis Lua脚本

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
@Service
public class InventoryService {

private static final String DEDUCT_STOCK_SCRIPT =
"local stock = redis.call('get', KEYS[1]) " +
"if stock == false or tonumber(stock) < tonumber(ARGV[1]) then " +
" return 0 " +
"end " +
"return redis.call('decrby', KEYS[1], ARGV[1])";

public boolean deductStock(String skuId, int quantity) {
String stockKey = "inventory:" + skuId;
DefaultRedisScript<Long> script = new DefaultRedisScript<>(DEDUCT_STOCK_SCRIPT, Long.class);

Long result = redisTemplate.execute(script,
Collections.singletonList(stockKey),
String.valueOf(quantity));

if (result == null || result < 0) {
throw new BusinessException("库存不足");
}

// 异步同步到数据库
syncToDatabase(skuId, quantity);

return true;
}
}

5.1.3 库存预热

预热策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class InventoryService {

@PostConstruct
public void warmUpInventory() {
// 从数据库加载库存到Redis
List<Inventory> inventories = inventoryMapper.selectAll();

for (Inventory inventory : inventories) {
String key = "inventory:" + inventory.getSkuId();
redisTemplate.opsForValue().set(key, String.valueOf(inventory.getStock()));
}
}
}

5.2 分布式事务

5.2.1 问题分析

分布式事务问题

  • 订单创建成功,库存扣减失败
  • 库存扣减成功,订单创建失败
  • 需要保证数据一致性

5.2.2 解决方案

方案1:TCC模式

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
@Service
public class OrderService {

@TccTransaction
public Order createOrder(OrderRequest request) {
// Try阶段
Order order = new Order();
order.setOrderId(generateOrderId());
order.setStatus(OrderStatus.PENDING);
orderMapper.insert(order);

// 预留库存
inventoryService.reserveStock(request.getSkuId(), request.getQuantity());

return order;
}

@Confirm
public void confirmOrder(Order order) {
// Confirm阶段:确认订单
order.setStatus(OrderStatus.CONFIRMED);
orderMapper.updateById(order);

// 确认库存扣减
inventoryService.confirmStock(order.getSkuId(), order.getQuantity());
}

@Cancel
public void cancelOrder(Order order) {
// Cancel阶段:取消订单
order.setStatus(OrderStatus.CANCELLED);
orderMapper.updateById(order);

// 回滚库存
inventoryService.cancelStock(order.getSkuId(), order.getQuantity());
}
}

方案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
@Service
public class OrderService {

@Transactional
public void createOrder(OrderRequest request) {
// 1. 创建订单
Order order = createOrderRecord(request);

// 2. 发送消息(事务消息)
orderMessageProducer.sendOrderCreatedMessage(order);

// 如果后续步骤失败,消息不会被消费
}
}

@Component
public class OrderMessageListener {

@KafkaListener(topics = "order-created", groupId = "inventory-processor")
public void handleOrderCreated(Order order) {
try {
// 扣减库存
inventoryService.deductStock(order.getSkuId(), order.getQuantity());
} catch (Exception e) {
// 库存扣减失败,发送补偿消息
compensationProducer.sendCompensationMessage(order);
}
}
}

方案3:最终一致性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class OrderService {

public void createOrder(OrderRequest request) {
// 1. 创建订单(本地事务)
Order order = createOrderRecord(request);

// 2. 发送消息到消息队列
orderEventProducer.publishOrderCreatedEvent(order);
}
}

@Component
public class InventoryEventHandler {

@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 异步扣减库存
inventoryService.deductStock(event.getSkuId(), event.getQuantity());
}
}

5.3 订单状态管理

5.3.1 状态机

订单状态

  • PENDING:待支付
  • PAID:已支付
  • SHIPPED:已发货
  • DELIVERED:已送达
  • CANCELLED:已取消
  • REFUNDED:已退款

状态机实现

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
public enum OrderStatus {
PENDING(0, "待支付"),
PAID(1, "已支付"),
SHIPPED(2, "已发货"),
DELIVERED(3, "已送达"),
CANCELLED(4, "已取消"),
REFUNDED(5, "已退款");

private final int code;
private final String desc;

// 状态转换规则
private static final Map<OrderStatus, Set<OrderStatus>> TRANSITIONS = new HashMap<>();

static {
TRANSITIONS.put(PENDING, Set.of(PAID, CANCELLED));
TRANSITIONS.put(PAID, Set.of(SHIPPED, REFUNDED));
TRANSITIONS.put(SHIPPED, Set.of(DELIVERED));
TRANSITIONS.put(DELIVERED, Set.of());
TRANSITIONS.put(CANCELLED, Set.of());
TRANSITIONS.put(REFUNDED, Set.of());
}

public boolean canTransitionTo(OrderStatus target) {
return TRANSITIONS.getOrDefault(this, Collections.emptySet()).contains(target);
}
}

6. 性能优化

6.1 缓存优化

6.1.1 多级缓存

缓存架构

1
本地缓存(Caffeine) → Redis缓存 → 数据库

实现

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
@Service
public class ProductService {

@Autowired
private Cache<String, Product> localCache;

@Autowired
private RedisTemplate<String, String> redisTemplate;

public Product getProduct(String productId) {
// 1. 查询本地缓存
Product product = localCache.getIfPresent(productId);
if (product != null) {
return product;
}

// 2. 查询Redis缓存
String cacheKey = "product:" + productId;
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
if (cacheValue != null) {
product = JSON.parseObject(cacheValue, Product.class);
localCache.put(productId, product);
return product;
}

// 3. 查询数据库
product = productMapper.selectById(productId);
if (product != null) {
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(product), 1, TimeUnit.HOURS);
localCache.put(productId, product);
}

return product;
}
}

6.1.2 缓存预热

预热策略

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class CacheWarmUp {

@PostConstruct
public void warmUp() {
// 预热热门商品
List<String> hotProducts = getHotProducts();
for (String productId : hotProducts) {
productService.getProduct(productId);
}
}
}

6.2 数据库优化

6.2.1 分库分表

分库分表策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class OrderService {

public void createOrder(Order order) {
// 根据用户ID分库
String dbKey = "order_db_" + (order.getUserId() % 4);

// 根据订单ID分表
String tableName = "order_" + (order.getOrderId().hashCode() % 16);

// 插入订单
orderMapper.insertOrder(dbKey, tableName, order);
}
}

6.2.2 读写分离

主从配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class DataSourceConfig {

@Bean
public DataSource dataSource() {
// 主库(写)
HikariDataSource master = new HikariDataSource();
master.setJdbcUrl("jdbc:mysql://master:3306/order_db");

// 从库(读)
HikariDataSource slave = new HikariDataSource();
slave.setJdbcUrl("jdbc:mysql://slave:3306/order_db");

// 读写分离
return new MasterSlaveDataSource(master, slave);
}
}

6.3 消息队列优化

6.3.1 批量处理

批量消费

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class OrderConsumer {

@KafkaListener(topics = "order-create", groupId = "order-processor")
public void processOrders(List<ConsumerRecord<String, String>> records) {
List<OrderRequest> requests = records.stream()
.map(r -> JSON.parseObject(r.value(), OrderRequest.class))
.collect(Collectors.toList());

// 批量处理
orderService.batchCreateOrders(requests);
}
}

7. 高可用设计

7.1 容错设计

7.1.1 服务降级

降级策略

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class OrderService {

@HystrixCommand(fallbackMethod = "createOrderFallback")
public Order createOrder(OrderRequest request) {
return doCreateOrder(request);
}

public Order createOrderFallback(OrderRequest request) {
// 降级处理:返回默认订单或提示信息
return new Order();
}
}

7.1.2 熔断机制

熔断配置

1
2
3
4
5
6
7
8
9
10
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
}
)
public Order createOrder(OrderRequest request) {
return doCreateOrder(request);
}

7.2 监控告警

7.2.1 性能监控

监控指标

  • QPS:每秒请求数
  • 响应时间:P50、P99、P999
  • 错误率:错误请求比例
  • 库存使用率:库存使用情况

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class OrderService {

@Autowired
private MeterRegistry meterRegistry;

public Order createOrder(OrderRequest request) {
Timer.Sample sample = Timer.start(meterRegistry);

try {
Order order = doCreateOrder(request);
meterRegistry.counter("order.create.success").increment();
return order;
} catch (Exception e) {
meterRegistry.counter("order.create.error").increment();
throw e;
} finally {
sample.stop(meterRegistry.timer("order.create.duration"));
}
}
}

7.2.2 告警机制

告警规则

  • 错误率 > 1%:告警
  • 响应时间 > 500ms:告警
  • 库存不足:告警

8. 实战案例

8.1 完整实现

8.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
25
26
27
28
29
30
@RestController
@RequestMapping("/api/order")
public class OrderController {

@Autowired
private OrderService orderService;

@PostMapping("/create")
public Response<Order> createOrder(@RequestBody OrderRequest request) {
try {
// 参数校验
validateRequest(request);

// 限流检查
if (!orderService.checkRateLimit(request.getUserId())) {
return Response.error("请求过于频繁,请稍后再试");
}

// 创建订单
Order order = orderService.createOrder(request);

return Response.success(order);
} catch (BusinessException e) {
return Response.error(e.getMessage());
} catch (Exception e) {
log.error("创建订单失败", e);
return Response.error("系统异常,请稍后再试");
}
}
}

8.1.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
@Service
public class OrderService {

@Autowired
private InventoryService inventoryService;

@Autowired
private ProductService productService;

@Autowired
private PriceService priceService;

@Autowired
private OrderMapper orderMapper;

@Autowired
private RedisTemplate<String, String> redisTemplate;

@Transactional
public Order createOrder(OrderRequest request) {
// 1. 查询商品信息
Product product = productService.getProduct(request.getProductId());

// 2. 校验库存
if (!inventoryService.checkStock(request.getSkuId(), request.getQuantity())) {
throw new BusinessException("库存不足");
}

// 3. 计算价格
Price price = priceService.calculatePrice(request);

// 4. 创建订单
Order order = new Order();
order.setOrderId(generateOrderId());
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setSkuId(request.getSkuId());
order.setQuantity(request.getQuantity());
order.setAmount(price.getTotalAmount());
order.setStatus(OrderStatus.PENDING);
order.setCreateTime(new Date());

orderMapper.insert(order);

// 5. 扣减库存
inventoryService.deductStock(request.getSkuId(), request.getQuantity());

// 6. 发送订单创建事件
orderEventPublisher.publishOrderCreatedEvent(order);

return order;
}
}

8.2 性能测试

8.2.1 压测结果

测试环境

  • 服务器:10台(8核16G)
  • 数据库:MySQL主从
  • 缓存:Redis集群

测试结果

  • QPS:10万+
  • 响应时间:P99 < 200ms
  • 错误率:< 0.1%

9. 总结

9.1 核心要点

  1. 架构设计:分层架构、服务拆分、数据分离
  2. 高并发处理:限流、削峰、异步处理
  3. 数据一致性:分布式锁、分布式事务、最终一致性
  4. 性能优化:缓存、数据库优化、消息队列
  5. 高可用设计:容错、降级、监控

9.2 关键设计

  1. 限流:多级限流,防止系统过载
  2. 削峰:消息队列削峰,提高吞吐量
  3. 缓存:多级缓存,提高性能
  4. 分布式锁:保证库存扣减一致性
  5. 异步处理:提高响应速度

9.3 最佳实践

  1. 分层设计:清晰的架构分层
  2. 服务拆分:微服务架构
  3. 数据分离:读写分离、分库分表
  4. 缓存策略:多级缓存、缓存预热
  5. 监控告警:实时监控、及时告警

相关文章