尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SpringBoot+Vue影院订票系统高并发实战

SpringBoot+Vue影院订票系统高并发实战 1. 项目背景与核心需求影院订票系统是典型的B/S架构应用需要同时满足影院管理端和用户端的业务需求。这个项目采用前后端分离架构后端基于SpringBootMyBatisMySQL技术栈前端使用Vue.js框架实现了从影片管理、排期设置到在线选座购票的全流程功能。在实际开发中这类系统面临几个关键挑战高并发座位锁定热门影片开售时需处理瞬时高并发请求事务一致性支付与座位状态变更必须保持原子性实时数据展示剩余座位数需要准确实时反馈给所有用户多端适配需要同时支持PC端和移动端访问2. 技术栈选型分析2.1 后端技术组合SpringBoot 2.7 MyBatis 3.5 MySQL 8.0的组合是经过验证的成熟方案SpringBoot优势自动配置简化了传统SSM框架的XML配置内嵌Tomcat容器便于部署Starter依赖机制统一管理第三方库版本Actuator端点提供系统监控能力MyBatis考量相比Hibernate更贴近SQL便于复杂查询优化动态SQL能力适合多条件筛选场景与PageHelper插件配合实现物理分页MySQL选型原因事务支持完善ACID特性行级锁适合订票系统的座位锁定场景通过EXPLAIN可以方便优化查询性能2.2 前端技术方案Vue 3.x Element Plus的组合提供组件化开发座位选择器、场次选择器等可复用组件Composition API提升代码组织性状态管理Pinia管理全局状态如用户登录信息本地存储记住用户偏好设置特殊功能实现使用WebSocket实现座位状态实时同步腾讯地图API集成显示影院位置3. 核心模块设计与实现3.1 数据库设计要点CREATE TABLE schedule ( id bigint NOT NULL AUTO_INCREMENT, movie_id bigint NOT NULL, hall_id int NOT NULL, show_time datetime NOT NULL, price decimal(10,2) NOT NULL, status tinyint DEFAULT 1, PRIMARY KEY (id), KEY idx_movie (movie_id), KEY idx_time (show_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE seat_order ( id bigint NOT NULL AUTO_INCREMENT, schedule_id bigint NOT NULL, seat_row int NOT NULL, seat_col int NOT NULL, user_id bigint DEFAULT NULL, order_id varchar(32) DEFAULT NULL, status tinyint NOT NULL COMMENT 0-可选 1-已锁定 2-已售出, lock_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_seat (schedule_id,seat_row,seat_col), KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关键设计考虑座位表使用联合唯一索引防止重复预订场次表建立时间索引加速查询状态字段使用tinyint而非字符串提高查询效率价格字段使用decimal避免浮点精度问题3.2 高并发座位处理采用乐观锁定时任务的方案Transactional public boolean lockSeats(Long scheduleId, ListSeatPosition seats, Long userId) { // 1. 检查座位状态 ListSeatOrder seatOrders seatOrderMapper.selectByScheduleAndPositions( scheduleId, seats); if(seatOrders.stream().anyMatch(s - s.getStatus() ! 0)){ throw new BusinessException(座位已被占用); } // 2. 批量锁定 String orderId generateOrderId(); seatOrderMapper.batchUpdateStatus( scheduleId, seats, 1, // 锁定状态 orderId, userId, LocalDateTime.now().plusMinutes(15)); // 15分钟支付时限 // 3. 延迟消息检查支付状态 rabbitTemplate.convertAndSend( order.delay.check, orderId, message - { message.getMessageProperties() .setDelay(15 * 60 * 1000); // 15分钟延迟 return message; }); return true; }配套措施Redis缓存热门场次的座位状态定时任务释放超时未支付座位前端轮询剩余座位数节流处理3.3 支付流程设计支付状态机实现stateDiagram-v2 [*] -- 待支付 待支付 -- 已取消: 超时未支付 待支付 -- 支付中: 发起支付 支付中 -- 已取消: 用户取消 支付中 -- 已支付: 支付成功 已支付 -- 已完成: 核销入场关键处理逻辑支付成功后异步通知更新订单状态使用本地事务表保证支付回调幂等性对账任务修复异常状态订单4. 典型问题解决方案4.1 座位状态同步延迟问题现象用户A看到座位可选但实际已被B锁定解决方案前端使用WebSocket接收实时状态更新后端采用发布-订阅模式广播变更加入版本号机制处理消息乱序实现代码GetMapping(/seat-status/{scheduleId}) public SseEmitter streamSeatStatus(PathVariable Long scheduleId) { SseEmitter emitter new SseEmitter(30 * 60 * 1000L); String subscriptionId schedule- scheduleId; seatStatusPublisher.addEmitter(subscriptionId, emitter); emitter.onCompletion(() - seatStatusPublisher.removeEmitter(subscriptionId)); emitter.onTimeout(() - seatStatusPublisher.removeEmitter(subscriptionId)); return emitter; }4.2 定时任务幂等性问题场景释放座位任务重复执行导致有效订单被取消解决方案使用分布式锁保证单节点执行任务记录执行日志状态变更增加条件判断Scheduled(cron 0 */1 * * * ?) Transactional public void releaseExpiredSeats() { // 获取分布式锁 String lockKey job:releaseSeats; boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 5, TimeUnit.MINUTES); if(!locked) return; try { ListSeatOrder expiredSeats seatOrderMapper .selectExpiredLocks(LocalDateTime.now()); expiredSeats.forEach(seat - { int updated seatOrderMapper.updateStatusIfMatch( seat.getId(), 1, // 期望原状态已锁定 0, // 新状态可用 seat.getVersion()); if(updated 0) { seatStatusPublisher.publish( seat.getScheduleId(), seat.getSeatRow(), seat.getSeatCol(), 0); } }); } finally { redisTemplate.delete(lockKey); } }5. 部署与性能优化5.1 生产环境配置推荐部署架构前端Nginx - 后端集群(2-4节点) - MySQL主从 - Redis哨兵关键配置项SpringBoot连接池配置spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000MyBatis二级缓存settings setting namecacheEnabled valuetrue/ setting namelocalCacheScope valueSTATEMENT/ /settings5.2 性能压测数据使用JMeter模拟测试4核8G服务器场景线程数平均响应时间错误率TPS查询场次100128ms0%420锁定座位50235ms1.2%180支付流程30310ms0.5%95优化手段添加Cacheable缓存热门影片数据使用Async异步处理非核心逻辑数据库查询字段精确指定避免SELECT *6. 扩展功能实现6.1 微信小程序端适配改造方案复用现有API接口增加JWT认证支持封装统一响应格式安全措施接口签名验证敏感数据脱敏频率限制如1秒内不能重复提交订单6.2 数据分析模块使用Elasticsearch实现用户行为日志收集热门影片分析上座率统计示例聚合查询{ size: 0, aggs: { popular_movies: { terms: { field: movieId, size: 5 }, aggs: { total_sales: { sum: { field: ticketCount } } } } } }7. 开发经验总结事务边界划定座位锁定与订单创建应放在不同事务中支付回调处理需要单独的事务配置缓存策略场次信息采用Cache-Aside模式座位状态采用Write-Through模式异常处理原则用户操作异常应给出明确提示系统异常需记录完整上下文重试机制需考虑幂等性调试技巧使用Arthas监控MyBatisSQL利用SpringBoot Actuator检查Bean加载集成测试使用Testcontainers实际开发中发现的最有价值经验是对于座位锁定这类高并发操作单纯依赖数据库行锁会导致性能瓶颈最终采用的Redis预检数据库确认异步通知三级方案在保证一致性的同时将并发处理能力提升了8倍。
返回列表