1. 抢票系统核心挑战:
- 瞬时高并发:百万级别用户同时请求
- 数据强一致性:扣除库存保证准确,防止超卖
- 系统稳定性:防止雪崩
- 涉及多方系统:查询系统、订单系统、支付系统、通知系统等
- 公平性:防止黄牛恶意刷票,保证用户公平抢票
2. 架构设计
- 客户端请求API网关
- 网关做限流(RateLimter)、防刷、路由(Apigee/Nginx)操作
- 订单服务:订单服务收到请求,先检查用户状态、余额等
- 订单服务调用库存服务,尝试扣减库存(库存服务是核心,必须高性能、高可用)
- 库存服务:扣减库存成功——》创建订单——》返回“抢票成功,请支付”
- 用户服务:抢票成功后发送消息到MQ:通知用户/超时支付任务
- 支付服务:用户支付——》支付服务回调——》订单服务更新状态——》通知库存服务确认库存扣减

3. 核心模块设计
3.1 库存模块(核心模块)
库存模块必须满足:
- 高并发读写
- 强一致性
- 防超卖
- 高性能
方案流程:Redis+MySQL+分布式锁
- 库存预热
系统启动时或者定时任务将库存从DB加载到redis。redis中存储总库存、已售库存、版本号(乐观锁用)
2. 扣减库存与库存回滚流程
使用Redis原子操作(INCR/DECR或者LUA脚本);
如下代码逻辑:首先初始化当前航班总库存到redis中,key是当前航班号,value是当前航班所有库存;
然后进行库存扣除:通过redis lua脚本判断当前库存和要扣除数量对比,如果扣除数量<当前库存则允许扣除库存 redis.call('decrby', key, quantity);
// 基于Redis的分布式库存管理 @Component public class TicketInventoryService { @Autowired private RedisTemplate<String, String> redisTemplate; // 初始化库存;flightId:被抢机票系统对应航班号;totalSeat:总库存 public void initInventory(String flightId, int totalSeats) { String key = "inventory:flight:" + flightId; redisTemplate.opsForValue().set(key, String.valueOf(totalSeats)); // 设置库存预热缓存 String stockKey = "stock:hot:" + flightId; redisTemplate.opsForHash().putAll(stockKey, Map.of("total", String.valueOf(totalSeats), "available", String.valueOf(totalSeats))); } // 扣减库存(使用Lua脚本保证原子性);flightId:航班号;quantity:扣减库存数量 public boolean deductInventory(String flightId, int quantity) { String luaScript = """ local key = KEYS[1] local quantity = tonumber(ARGV[1]) local current = tonumber(redis.call('get', key) or 0) //current当前总库存 if current >= quantity then redis.call('decrby', key, quantity) return 1 else return 0 end """; RedisScript<Long> script = RedisScript.of(luaScript, Long.class); Long result = redisTemplate.execute(script, Collections.singletonList("inventory:flight:" + flightId), String.valueOf(quantity)); return result == 1; } // 库存回滚--订单取消或者支付超时后恢复库存 public void rollbackInventory(String flightId, int quantity) { redisTemplate.opsForValue().increment( "inventory:flight:" + flightId, quantity); } }
3. 最终一致性
redis扣减库存成功后,异步写入MySQL中,如果写入失败,则通过重试+补偿机制保证最终一致性
4. 分布式锁
为了防止并发问题,使用redis lock来保证
5. 库存回滚
订单超时未支付——》释放缓存
通过MQ延迟消息实现
3.2 订单抢票模块
方案流程:Redis分布式锁+雪花算法+异步MQ
基于航班号为key的redis分布式锁——》调用库存模块库存检验判断当前库存——》基于雪花算法生成订单号——》调用库存模块进行库存扣除——》使用MQ异步处理订单
@Service public class TicketGrabService { @Autowired private RedissonClient redissonClient; @Autowired private TicketInventoryService inventoryService; @Autowired private TicketOrderService orderService; @Autowired private KafkaTemplate kafkaTemplate; // 分布式锁实现抢票 public GrabResult grabTicket(GrabRequest request) { String lockKey = "ticket:lock:" + request.getFlightNo(); //航班号作为key RLock lock = redissonClient.getLock(lockKey); try { // 尝试获取锁,等待3秒,锁有效期10秒 if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 1. 检查库存 if (!inventoryService.checkInventory(request)) { return GrabResult.fail("票已售罄"); } // 2. 雪花算法生成订单号;雪花算法是生成分布式唯一ID的核心方案,能高效生成有序、唯一的Long型ID String orderNo = generateOrderNo(); // 3. 扣减库存 if (inventoryService.reduceInventory(request.getFlightNo(), 1)) { // 4. 异步处理订单 Order order=buildOrder(request,orderNo); //创建订单 kafkaTemplate.send(order); //订单异步落库;之后通过kafka消费者接收到消息后进行异步落库处理 return GrabResult.success(orderNo); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } return GrabResult.fail("抢票失败"); } // 生成订单号(雪花算法) private String generateOrderNo() { Snowflake snowflake = IdUtil.getSnowflake(1, 1); return "TKT" + DateUtil.format(new Date(), "yyyyMMdd") + snowflake.nextIdStr().substring(0, 10); } }
总结抢票系统关键技术解决方案
1.高并发解决方案:
1.1 缓存预热--抢票前预热数据到redis
1.2令牌桶限流--(基于Guava/Redis RateLimiter限流策略)
@Component public class RateLimitService { private final RateLimiter rateLimiter = RateLimiter.create(1000); // 每秒1000个请求 public boolean tryAcquire() { return rateLimiter.tryAcquire(); } // 分布式限流(基于Redis) public boolean distributedRateLimit(String key, int limit, int timeout) { String luaScript = "local current = redis.call('incr', KEYS[1]) " + "if current == 1 then " + " redis.call('expire', KEYS[1], ARGV[1]) " + "end " + "return current <= tonumber(ARGV[2])"; return (Boolean) redisTemplate.execute( new DefaultRedisScript<>(luaScript, Boolean.class), Collections.singletonList(key), timeout, limit ); } }
2 防超卖解决方案
2.1 Redis Lua脚本原子操作
public class AtomicInventoryService { private static final String REDUCE_INVENTORY_SCRIPT = "local key = KEYS[1] " + "local quantity = tonumber(ARGV[1]) " + "local available = tonumber(redis.call('get', key) or '0') " + "if available >= quantity then " + " redis.call('decrby', key, quantity) " + " return 1 " + "else " + " return 0 " + "end"; public boolean atomicReduce(String key, int quantity) { Long result = (Long) redisTemplate.execute( new DefaultRedisScript<>(REDUCE_INVENTORY_SCRIPT, Long.class), Collections.singletonList(key), String.valueOf(quantity) ); return result == 1; } }
2.2 数据库乐观锁
@Repository public class TicketOrderRepository { @Transactional public boolean createOrderWithOptimisticLock(Order order) { // 版本号控制 String sql = "UPDATE flight_seats SET " + "available = available - 1, " + "version = version + 1 " + "WHERE flight_no = ? " + "AND available > 0 " + "AND version = ?"; int rows = jdbcTemplate.update( sql, order.getFlightNo(), order.getVersion() ); return rows > 0; } }
3 可靠性保证 -- 重试 降级
@Component public class ReliabilityService { // 1. 重试机制 @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2)) public OrderResult createOrderWithRetry(OrderRequest request) { return ticketService.createOrder(request); } // 2. 降级策略 @HystrixCommand(fallbackMethod = "fallbackGrabTicket", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20") }) public GrabResult grabTicketWithFallback(GrabRequest request) { return grabService.grabTicket(request); } public GrabResult fallbackGrabTicket(GrabRequest request) { // 返回友好提示或加入异步队列 return GrabResult.fail("系统繁忙,请稍后重试"); } }
4 防刷策略 -- 验证码 分析用户抢票频率
@Component public class AntiBrushService { // IP限流 public boolean checkIpLimit(String ip) { String key = "limit:ip:" + ip + ":" + LocalDate.now(); return rateLimitService.distributedRateLimit(key, 100, 86400); } // 用户行为分析 public boolean isMaliciousUser(String userId) { String patternKey = "user:pattern:" + userId; // 分析用户抢票频率、成功率等特征 // 使用机器学习模型判断 return false; } // 验证码验证 public boolean verifyCaptcha(String sessionId, String captcha) { String key = "captcha:" + sessionId; String storedCaptcha = (String) redisTemplate.opsForValue().get(key); return captcha != null && captcha.equalsIgnoreCase(storedCaptcha); } }
5 监控与日志 grafana(数据可视化分析平台)和prometheus(数据采集存储时序数据库)
浙公网安备 33010602011771号