当前位置: 首页 > news >正文

抢票系统设计

1. 抢票系统核心挑战:

  • 瞬时高并发:百万级别用户同时请求
  • 数据强一致性:扣除库存保证准确,防止超卖
  • 系统稳定性:防止雪崩
  • 涉及多方系统:查询系统、订单系统、支付系统、通知系统等
  • 公平性:防止黄牛恶意刷票,保证用户公平抢票

2. 架构设计

  • 客户端请求API网关
  • 网关做限流(RateLimter)、防刷、路由(Apigee/Nginx)操作
  • 订单服务:订单服务收到请求,先检查用户状态、余额等
  • 订单服务调用库存服务,尝试扣减库存(库存服务是核心,必须高性能、高可用)
  • 库存服务:扣减库存成功——》创建订单——》返回“抢票成功,请支付”
  • 用户服务:抢票成功后发送消息到MQ:通知用户/超时支付任务
  • 支付服务:用户支付——》支付服务回调——》订单服务更新状态——》通知库存服务确认库存扣减

 image

 3. 核心模块设计

3.1 库存模块(核心模块)

 库存模块必须满足:

  • 高并发读写
  • 强一致性
  • 防超卖
  • 高性能

方案流程:Redis+MySQL+分布式锁

  1.  库存预热

    系统启动时或者定时任务将库存从DB加载到redis。redis中存储总库存、已售库存、版本号(乐观锁用)

   2. 扣减库存与库存回滚流程

    使用Redis原子操作(INCR/DECR或者LUA脚本);

    如下代码逻辑:首先初始化当前航班总库存到redis中,key是当前航班号,value是当前航班所有库存;

          然后进行库存扣除:通过redis lua脚本判断当前库存和要扣除数量对比,如果扣除数量<当前库存则允许扣除库存 redis.call('decrby', key, quantity);                

// 基于Redis的分布式库存管理
@Component
public class TicketInventoryService {@Autowiredprivate 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 thenredis.call('decrby', key, quantity)return 1elsereturn 0end""";
        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 {@Autowiredprivate RedissonClient redissonClient;@Autowiredprivate TicketInventoryService inventoryService;@Autowiredprivate TicketOrderService orderService;@Autowiredprivate KafkaTemplate kafkaTemplate;// 分布式锁实现抢票public GrabResult grabTicket(GrabRequest request) {String lockKey = "ticket:lock:" + request.getFlightNo(); //航班号作为keyRLock 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型IDString 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);}
}
View Code

 

 

总结抢票系统关键技术解决方案

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);}
}
View Code

 

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;}
}
View Code

 

2.2 数据库乐观锁

@Repository
public class TicketOrderRepository {@Transactionalpublic 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;}
}
View Code

 

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("系统繁忙,请稍后重试");}
}
View Code

 

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);}
}
View Code

 

5 监控与日志  grafana(数据可视化分析平台)和prometheus(数据采集存储时序数据库)

 

http://www.jsqmd.com/news/307834/

相关文章:

  • 2026年工程管理软件推荐:项目分散场景深度评测,解决成本超支与协同痛点并附排名
  • 2026年专业防爆箱优质供应商推荐指南
  • 2026年工程管理软件推荐:横向对比与稳定性评价,涵盖现场管理与远程办公多元场景
  • 学霸同款8个AI论文工具,自考论文格式规范轻松搞定!
  • 2026年市场比较好的清障车实力厂家排行榜,常奇清障车/帕菲特清障车/直臂高空作业车/高空作业车,清障车实力厂家哪家好
  • 2026优质防火门推荐榜安全合规之选
  • 学长亲荐!MBA必备TOP8 AI论文工具测评
  • 学长亲荐9个AI论文平台,继续教育学生必备!
  • 盘点2026年武汉地区优质加气块定制生产商,排行前列的加气块10年质保有保障
  • 深度解析商用咖啡机器人关键技术与2026年主流产品应用指南
  • 2026国内最新药材采购供货厂家top5推荐!江西等地优质道地药材/医药流通/药都服务企业权威榜单发布,合规高效助力中医药采购
  • 2026年钢板仓成型设备品牌排名,这些厂家值得关注
  • 2026年好用的CZ型钢一体机设备品牌,推荐靠谱供应商
  • 闪唤到家家政公司专业吗,哈尔滨地区性价比高不高?
  • 聊聊哈尔滨下水道疏通、地漏疏通,哪家口碑好、性价比高且靠谱?
  • 2026年湖南地区医用冷冻冷藏防爆冰箱供应商排名,靠谱品牌推荐
  • 2026年元宵机器供应企业排名,靠谱的厂家费用怎么算
  • 寻找优质扩香器?不妨看看这些口碑工厂,藤条精油/疗愈香氛/蜡烛香薰/蜡片香氛/固体香氛/旅行香氛,扩香器ODM厂家推荐
  • 构建一个高精度 Flutter 计时器:深入解析 Timer、状态同步与 UI 响应式设计
  • 专科生必看!10个高效降AIGC工具推荐,轻松降低AI痕迹
  • 2026年杭州评价好的母猫绝育医院哪家好,宠物医生/宠物骨科/母猫绝育/宠物医院/宠物眼科,猫咪绝育医生专家推荐
  • 2025导电滑环工厂排名出炉!谁才是行业佼佼者?过孔滑环/电环/光纤滑环/旋转接头/气电滑环,导电滑环生产厂家如何选
  • mfc40loc.dll文件在系统内缺少 无法运行问题 免费下载
  • mfc70chs.dll文件丢失找不到 免费下载方法分享
  • 2026 年最新家电维修品牌推荐排行榜:空调维修 / 冰箱维修 / 洗衣机维修 / 太阳能维修/ 燃气灶维修优质服务商平台权威榜单
  • 2026年评价高的电梯公司推荐:电梯保养、电梯更新、鲁泰电梯保养、鲁泰电梯改造、鲁泰电梯更新、鲁泰电梯维修、电梯改造选择指南
  • 看看天津企业合规法律服务律所,口碑好的有哪些
  • 2026年京津冀鲁人力资源外包推荐厂商排名,靠谱的品牌有哪些
  • 深入解析:LeetCode算法日记 - Day 108: 01背包
  • 2026惠州金融行业logo设计优质公司推荐