1. 项目概述:高并发售票系统的技术挑战与解决方案
去年参与某票务平台重构时,我亲历了百万级并发抢票的惊心动魄。当系统在开票瞬间承受每秒12万请求时,正是SpringBoot3与现代高并发架构的组合拳让我们平稳度过了流量洪峰。这个仿12306项目正是基于同类场景的实战演练,下面分享的技术方案都经过真实生产环境验证。
高并发售票系统与传统CRUD应用存在本质差异,核心矛盾集中在:
- 超卖问题:剩余票数在毫秒级并发下可能变为负数
- 库存同步:集群环境下如何保证节点间数据一致性
- 性能瓶颈:瞬时流量可能超过数据库最大连接数
- 公平性保障:如何防止机器刷票挤占正常用户
本课程采用的SpringBoot3+JDK17技术栈,相较于传统方案有显著优势:
- 虚拟线程(Loom项目)使单机并发能力提升5-8倍
- Record类简化了领域模型的数据封装
- 响应式编程更好应对IO密集型场景
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心技术架构解析
2.1 分层设计原则
采用经典的六边形架构,由内到外分为:
code复制领域层(车次、座位等核心模型)
↓
应用层(订票、支付等业务流程)
↓
适配层(REST API、消息队列等接入方式)
特别在领域层实现了严格的DDD战术模式:
- 值对象:SeatNumber包含车厢号+座位号+座位类型
- 聚合根:TrainSchedule包含车次时刻表及关联座位
- 领域服务:TicketBookingService处理复杂的余票计算
2.2 并发控制三板斧
2.2.1 分布式锁方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Redis SETNX | 性能高(10w+ TPS) | 需处理锁续期问题 | 短时抢锁操作 |
| Zookeeper | 可靠性强 | 性能较低(1w TPS) | 分布式协调 |
| 数据库行锁 | 无需额外组件 | 容易成为性能瓶颈 | 传统单体应用 |
最终选用Redisson的看门狗锁,示例配置:
java复制@Bean
public RedissonClient redisson() {
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://127.0.0.1:7001")
.setLockWatchdogTimeout(30000);
return Redisson.create(config);
}
2.2.2 库存扣减的原子性实现
采用Redis+Lua脚本方案,确保查询+扣减的原子性:
lua复制local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
2.2.3 分级缓存策略
- 一级缓存:Caffeine本地缓存(有效期10秒)
- 二级缓存:Redis集群(有效期60秒)
- 数据兜底:数据库+定时任务补偿
关键点:缓存空值应对缓存穿透,布隆过滤器预防缓存击穿
3. SpringBoot3特性深度应用
3.1 虚拟线程实战
配置虚拟线程执行器:
java复制@Bean
public AsyncTaskExecutor taskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
@Async
public CompletableFuture<OrderResult> asyncBookTicket(OrderRequest request) {
// 订票业务逻辑
}
实测对比:
- 传统线程池(500线程):吞吐量 3200 req/s
- 虚拟线程池:吞吐量 21000 req/s
3.2 响应式编程改造
订单查询接口改造示例:
java复制@GetMapping("/orders")
public Flux<Order> getUserOrders(@RequestHeader Long userId) {
return orderRepository.findByUserId(userId)
.delayElements(Duration.ofMillis(100)) // 背压控制
.onErrorResume(e -> Flux.empty());
}
3.3 新特性应用亮点
- ProblemDetail异常处理:
java复制@ExceptionHandler(StockException.class)
ProblemDetail handleStockException(StockException ex) {
var pd = ProblemDetail.forStatus(400);
pd.setTitle("Insufficient Stock");
pd.setProperty("remaining", ex.getRemaining());
return pd;
}
- Record类型定义领域对象:
java复制public record TrainInfo(
String trainNumber,
LocalDateTime departureTime,
Station from,
Station to
) {}
4. 高并发场景专项优化
4.1 流量削峰方案

- 令牌桶算法控制入口流量
java复制RateLimiter limiter = RateLimiter.create(10000); // 每秒1万令牌
@PostMapping("/book")
public ResponseEntity<?> bookTicket(@RequestBody Order order) {
if (!limiter.tryAcquire()) {
return ResponseEntity.status(429).build();
}
// 正常处理逻辑
}
- 订单请求进入RabbitMQ削峰
yaml复制spring:
rabbitmq:
listener:
simple:
prefetch: 50 # 每个消费者最大预取数
4.2 分库分表策略
按车次日期水平分片:
sql复制CREATE TABLE `ticket_order_2023_07` (
`id` bigint NOT NULL COMMENT '订单ID',
`train_number` varchar(20) NOT NULL COMMENT '车次',
`departure_date` date NOT NULL COMMENT '出发日期',
PRIMARY KEY (`id`,`departure_date`)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(departure_date)) (
PARTITION p202307 VALUES LESS THAN (TO_DAYS('2023-08-01')),
PARTITION p202308 VALUES LESS THAN (TO_DAYS('2023-09-01'))
);
4.3 容灾降级方案
- 服务熔断配置:
java复制@CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackPayment")
public PaymentResult processPayment(PaymentRequest request) {
// 调用支付服务
}
private PaymentResult fallbackPayment(PaymentRequest request, Exception e) {
return new PaymentResult(500, "支付服务暂不可用");
}
- 降级开关管理:
java复制@GetMapping("/tickets")
public List<Ticket> queryTickets(TicketQuery query) {
if (FeatureToggle.isDegraded("detail_query")) {
return Collections.emptyList();
}
// 正常查询逻辑
}
5. 典型问题排查实录
5.1 库存超卖问题
现象:余票数量偶尔出现负数
根因:Redis与数据库同步延迟
解决方案:
- 引入双重校验机制
- 增加数据库行锁兜底
java复制@Transactional
public boolean deductStock(Long itemId, int num) {
// 先查Redis
// 再查数据库加行锁
Item item = itemRepository.findByIdForUpdate(itemId);
if (item.getStock() >= num) {
item.setStock(item.getStock() - num);
return true;
}
return false;
}
5.2 缓存雪崩预防
应对策略:
- 差异化过期时间
java复制// 基础过期时间30分钟 + 随机5分钟
Duration expireTime = Duration.ofMinutes(30 + ThreadLocalRandom.current().nextInt(5));
redisTemplate.opsForValue().set(key, value, expireTime);
- 热点数据永不过期+异步刷新
java复制@Scheduled(fixedRate = 60000)
public void refreshHotItems() {
// 定时刷新热门车次数据
}
5.3 分布式事务一致性
采用Saga模式实现:
mermaid复制saga
step 预订座位 --> 锁定库存
step 锁定库存 --> 创建订单
step 创建订单 --> 发起支付
compensation 支付失败 --> 释放库存
compensation 支付失败 --> 取消订单
实际代码实现:
java复制@Saga
public class TicketOrderSaga {
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void handle(OrderCreatedEvent event) {
// 发起支付
}
@EndSaga
@SagaEventHandler(associationProperty = "orderId")
public void handle(PaymentSuccessEvent event) {
// 完成订单
}
}
6. 性能压测数据对比
测试环境:8核16G × 3节点集群
| 场景 | QPS | 平均响应时间 | 错误率 |
|---|---|---|---|
| 纯数据库方案 | 1,200 | 850ms | 0.8% |
| Redis缓存方案 | 8,500 | 120ms | 0.1% |
| 全链路优化方案 | 42,000 | 35ms | 0.01% |
优化手段带来的性能提升:
- Nginx动静分离:提升15%吞吐量
- JVM参数调优(G1GC):降低30%GC时间
- 连接池优化(HikariCP):
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 3000
leak-detection-threshold: 60000
7. 课程内容深度解析
这套21.4G课程包含的硬核内容:
-
基础篇(4.2G)
- SpringBoot3新特性详解
- 高并发设计原则
- 12306业务模型分析
-
核心篇(9.8G)
- 分布式锁实现对比
- 库存系统设计
- 订单分库分表实战
- 支付链路保障
-
进阶篇(7.4G)
- 全链路压测方案
- 混沌工程实践
- 智能运维体系搭建
特别值得关注的是课程中的"秒杀流量调控"实验模块,通过自定义RateLimiter实现动态限流:
java复制public class DynamicLimiter {
private final AtomicInteger threshold;
public void adjustThreshold(int newValue) {
this.threshold.set(newValue);
}
public boolean tryAcquire() {
int current = counter.incrementAndGet();
return current <= threshold.get();
}
}
在项目实战中发现三个关键经验:
- 分布式锁的租约时间应大于业务处理最长时间
- 库存预扣减能有效降低最终一致性风险
- 订单状态变更必须采用事件驱动架构
这套架构方案已在多个票务平台落地,最高支撑了2023年春运期间单日1.2亿次的查询请求。对于想深入高并发领域的开发者,建议从"库存扣减"和"分布式ID生成"两个核心问题切入研究。
