1. 项目概述
这个火车票订票系统是一个典型的B/S架构企业级应用,采用前后端分离设计模式。前端使用Vue.js构建响应式用户界面,后端基于SpringBoot框架提供RESTful API服务,数据持久层采用MySQL关系型数据库。系统实现了火车票查询、预订、支付、退改签等核心业务流程,同时包含用户管理、订单管理、车次管理等后台功能模块。
我在实际开发中发现,这类系统最关键的挑战在于高并发场景下的票务库存控制和分布式事务处理。举个例子,当春运期间大量用户同时抢购同一车次车票时,系统必须保证数据一致性和服务可用性。我们采用了Redis分布式锁结合乐观锁机制来解决这个问题,具体实现会在后文详细说明。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型解析
2.1 后端技术栈
SpringBoot 2.7.x作为基础框架,主要基于以下考虑:
- 自动配置特性大幅减少XML配置
- 内嵌Tomcat服务器简化部署
- 与Spring生态无缝集成(Spring Security, Spring Data JPA等)
- 完善的监控端点(Actuator)便于生产环境运维
数据库选用MySQL 8.0,关键配置参数:
sql复制# 事务隔离级别设置为REPEATABLE-READ
transaction-isolation = REPEATABLE-READ
# 连接池大小根据服务器配置调整
innodb_buffer_pool_size = 4G
2.2 前端技术栈
Vue 3.x + Element Plus组件库的组合优势:
- 组合式API提升代码可维护性
- Vite构建工具显著加快开发热更新速度
- 按需导入减少打包体积
- 完善的表单验证和UI组件满足业务需求
典型页面加载性能优化方案:
javascript复制// vite.config.js 配置示例
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'element-plus': ['element-plus'],
'vue-related': ['vue', 'vue-router', 'vuex']
}
}
}
}
})
3. 核心业务逻辑实现
3.1 票务库存控制
采用三级库存管理机制:
- 数据库层:使用SELECT...FOR UPDATE实现行级锁
- 应用层:通过Redis分布式锁控制并发
- 缓存层:Redis原子操作保证库存扣减
关键代码片段:
java复制// 分布式锁实现
public boolean lock(String lockKey, String requestId, int expireTime) {
return redisTemplate.opsForValue().setIfAbsent(
lockKey,
requestId,
Duration.ofSeconds(expireTime)
);
}
// 乐观锁更新库存
@Transactional
public boolean reduceStock(Long ticketId, int quantity) {
Ticket ticket = ticketRepository.findById(ticketId).orElseThrow();
if (ticket.getStock() < quantity) {
return false;
}
int updated = ticketRepository.reduceStockWithVersion(
ticketId,
quantity,
ticket.getVersion()
);
return updated > 0;
}
3.2 订单状态机设计
使用状态模式管理订单生命周期:
code复制待支付 --支付成功--> 已支付
已支付 --出票成功--> 已完成
待支付 --超时未支付--> 已取消
已支付 --用户退票--> 已退款
状态转换检查要点:
- 支付超时时间配置(建议30分钟)
- 退票手续费计算规则
- 出票失败补偿机制
4. 系统安全设计
4.1 认证授权方案
JWT + Spring Security实现方案:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
4.2 防刷票策略
多层防护机制:
- 图形验证码:登录和下单前验证
- 限流策略:Guava RateLimiter控制接口调用频率
- 行为分析:识别异常购票模式(如短时间内大量查询)
5. 性能优化实践
5.1 数据库优化
索引设计原则:
- 查询条件字段必建索引(如车次、日期)
- 联合索引遵循最左前缀原则
- 避免过度索引影响写入性能
执行计划分析示例:
sql复制EXPLAIN SELECT * FROM tickets
WHERE train_no = 'G123'
AND departure_date = '2023-12-25'
AND status = 'AVAILABLE';
5.2 缓存策略
多级缓存架构:
- 本地缓存(Caffeine):缓存静态数据
- 分布式缓存(Redis):缓存热点数据
- 数据库缓存(MySQL Query Cache)
缓存击穿解决方案:
java复制public Ticket getTicketWithCache(Long id) {
String cacheKey = "ticket:" + id;
Ticket ticket = redisTemplate.opsForValue().get(cacheKey);
if (ticket == null) {
synchronized (this) {
ticket = redisTemplate.opsForValue().get(cacheKey);
if (ticket == null) {
ticket = ticketRepository.findById(id).orElseThrow();
redisTemplate.opsForValue().set(
cacheKey,
ticket,
Duration.ofMinutes(30)
);
}
}
}
return ticket;
}
6. 部署方案
6.1 容器化部署
Docker Compose编排示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: railway
ports:
- "3306:3306"
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
6.2 高可用方案
生产环境建议配置:
- Nginx负载均衡 + Keepalived
- Redis哨兵模式
- MySQL主从复制
- 应用集群至少3节点
7. 常见问题排查
7.1 事务失效场景
典型case及解决方案:
- 异常未抛出:检查是否catch后未重新抛出
- 方法访问权限:必须是public方法
- 自调用问题:避免同类中方法互相调用
7.2 跨域问题处理
Vue前端配置示例:
javascript复制// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
SpringBoot后端配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*");
}
}
8. 答辩常见问题
技术面试高频问题:
-
如何保证票务系统的数据一致性?
- 回答要点:ACID特性、分布式事务方案(如TCC)、补偿机制
-
系统能承受多大的并发量?
- 回答要点:压测数据(如JMeter测试结果)、优化手段(缓存、异步处理)
-
支付模块如何设计?
- 回答要点:状态机设计、对账机制、第三方支付对接
实际开发中我们发现,采用Elasticsearch实现车次模糊搜索可以显著提升查询体验。具体实现是在车次信息变更时同步更新ES索引,查询时优先走ES再查详细数据
