1. 项目概述
这个电影订票及评论网站是我去年为一个本地影院客户开发的线上平台,核心目标是解决传统影院线下排队购票效率低、用户反馈渠道单一的问题。系统采用前后端分离架构,后端基于SpringBoot 2.7.4构建,前端使用Vue 3组合式API开发,数据库选用MySQL 8.0的InnoDB集群方案。上线三个月后,影院周末场次的线上售票占比从12%提升到58%,验证了技术方案的可行性。
技术选型心得:放弃传统的SSM组合而直接采用SpringBoot Starter简化配置,是因为影院系统需要快速迭代。实测证明,SpringBoot的自动配置特性让开发效率提升了40%左右。
2. 核心架构设计
2.1 分层架构解析
系统采用经典的三层架构,但在数据访问层做了特殊优化:
- 表现层:Vue 3 + Element Plus实现响应式布局
- 业务层:SpringBoot + 自定义Starter
- 数据层:MyBatis-Plus + MySQL读写分离
java复制// 典型的分层调用示例
@RestController
@RequestMapping("/api/ticket")
public class TicketController {
@Autowired
private TicketService ticketService; // 业务层接口
@PostMapping
public Result bookTicket(@Valid @RequestBody BookDTO dto) {
return ticketService.createOrder(dto);
}
}
2.2 数据库设计要点
影厅座位采用位图存储方案,将每个影厅的座位状态压缩存储为BLOB类型。以IMAX厅为例(座位数通常不超过300),一个场次的座位状态仅需38字节:
sql复制CREATE TABLE `t_screening` (
`id` BIGINT PRIMARY KEY,
`seat_map` BLOB NOT NULL COMMENT '位图存储的座位状态',
`version` INT DEFAULT 0 COMMENT '乐观锁版本号'
) ENGINE=InnoDB;
踩坑记录:初期使用JSON数组存储座位状态,在《阿凡达》首映时出现200ms的序列化延迟。改用位图后查询耗时稳定在5ms内。
3. 关键功能实现
3.1 高并发订票解决方案
采用Redis分布式锁 + 本地缓存二级校验的方案:
- 第一层:Redis SETNX锁(设置300ms过期时间)
- 第二层:Caffeine本地缓存最近5分钟售罄场次
- 最终校验:MySQL乐观锁更新
java复制public boolean lockSeats(Long screeningId, List<Integer> seats) {
String lockKey = "lock:screening:" + screeningId;
try {
// 尝试获取分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 300, TimeUnit.MILLISECONDS);
if (Boolean.TRUE.equals(locked)) {
// 本地缓存校验
if (localCache.getIfPresent(screeningId) != null) {
return false;
}
// 数据库最终操作
return seatMapper.updateSeats(screeningId, seats) > 0;
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
3.2 实时评论系统
使用WebSocket实现评论实时推送,结合敏感词过滤服务:
- 建立WS连接时订阅影厅频道
- 前端提交评论后先走敏感词过滤
- 通过Spring的SimpMessagingTemplate广播
javascript复制// 前端WS连接示例
const socket = new SockJS('/comment-websocket');
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
stompClient.subscribe('/topic/movie/' + movieId, (message) => {
const comment = JSON.parse(message.body);
addCommentToDOM(comment);
});
});
性能优化:实测发现频繁的WS消息会导致移动端发热,后来添加了消息合并机制(每200ms批量发送一次)。
4. 可视化数据分析
4.1 ECharts集成方案
通过自定义SpringBoot Starter简化ECharts集成:
- 创建
echarts-spring-boot-starter模块 - 自动配置Bean注入Option构建器
- 提供TypeScript类型声明合并
typescript复制// 票房统计组件示例
import { init } from 'echarts/core';
const chart = init(document.getElementById('chart'));
chart.setOption({
dataset: {
source: await fetchBoxOfficeData()
},
series: [{
type: 'heatmap',
encode: {
x: 'time',
y: 'hall',
value: 'sales'
}
}]
});
4.2 数据缓存策略
采用多级缓存提升图表响应速度:
- 原始数据:Redis缓存2小时
- 聚合结果:Caffeine缓存30分钟
- 最终图表:浏览器本地存储1天
缓存键设计规则:chart:${type}:${dimensions}:${timeRange}
5. 部署与监控
5.1 容器化部署
使用Docker Compose定义服务栈:
yaml复制version: '3.8'
services:
app:
image: openjdk:17-jdk
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
redis:
image: redis:6-alpine
ports:
- "6379:6379"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
5.2 监控方案
- SpringBoot Actuator暴露/metrics端点
- Prometheus采集指标
- Grafana配置看板
关键监控指标:
- 订票接口P99响应时间
- WebSocket连接数
- 缓存命中率
6. 典型问题排查
6.1 座位锁定失效
现象:高并发时出现座位超卖
排查过程:
- 检查Redis锁日志发现网络抖动导致过期时间异常
- 本地缓存与DB状态不一致
解决方案:
- 将Redis锁过期时间从300ms调整为500ms
- 添加Redisson看门狗机制
6.2 评论延迟
现象:高峰期评论显示延迟达5秒
根本原因:
- Kafka消费者配置的并发数不足
- 敏感词过滤服务响应变慢
优化措施:
- 将Kafka消费者线程数从2提升到8
- 对敏感词DFA算法进行预编译
7. 扩展建议
- 推荐系统:基于用户的观影历史实现协同过滤推荐
- 动态定价:根据上座率自动调整票价
- 小程序端:开发微信小程序覆盖更多用户
这个项目让我深刻体会到,即使是常规业务系统,在细节处理上也能做出技术深度。比如座位锁定方案就迭代了三个版本,从最初的简单锁表到现在的多级校验,每次优化都带来了明显的性能提升。