fix bugs
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-12-11 16:56:19 +08:00
parent ab69968bda
commit 5b806e29b7
45 changed files with 13744 additions and 6364 deletions

View File

@@ -0,0 +1,265 @@
# 赛程编排页面加载性能优化
## 问题描述
用户反馈:点击打开编排页面(`http://localhost:2888/api/martial/project/detail?id=200`)时出现大批量数据库查询,导致页面加载缓慢。
## 原问题分析
### 原实现方式MartialScheduleServiceImpl.java:149-258
```java
public ScheduleResultDTO getScheduleResult(Long competitionId) {
// 1. 查询所有分组
List<MartialScheduleGroup> groups = scheduleGroupMapper.selectList(...);
// 2. 查询所有编排明细
List<MartialScheduleDetail> details = scheduleDetailMapper.selectList(...);
// 3. 查询所有参赛者(使用 IN 查询)
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(...);
// 4. 在内存中进行数据组装
// ...
}
```
**性能问题**
- 执行了 **3 次独立的数据库查询**
- <20><><EFBFBD>然使用了 IN 查询避免了 N+1 问题,但仍需要多次网络往返
- 数据库需要执行 3 次查询计划,查询优化器无法统一优化
- 数据传输量大,需要多次网络 IO
## 优化方案
### 使用单次 JOIN 查询获取所有数据
#### 1. 创建优化的 VO <20><>
新建文件:`ScheduleGroupDetailVO.java`
```java
@Data
public class ScheduleGroupDetailVO implements Serializable {
// 分组信息
private Long groupId;
private String groupName;
private String category;
private Integer projectType;
private Integer totalTeams;
private Integer totalParticipants;
private Integer displayOrder;
// 编排明细信息
private Long detailId;
private Long venueId;
private String venueName;
private String timeSlot;
// 参赛者信息
private Long participantId;
private String organization;
private String checkInStatus;
private String scheduleStatus;
private Integer performanceOrder;
}
```
#### 2. 添加自定义 Mapper 方法
`MartialScheduleGroupMapper.java` 中添加:
```java
public interface MartialScheduleGroupMapper extends BaseMapper<MartialScheduleGroup> {
/**
* 查询赛程编排的完整详情一次性JOIN查询优化性能
*/
List<ScheduleGroupDetailVO> selectScheduleGroupDetails(@Param("competitionId") Long competitionId);
}
```
#### 3. 实现优化的 SQL 查询
`MartialScheduleGroupMapper.xml` 中实现:
```xml
<select id="selectScheduleGroupDetails" resultType="org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO">
SELECT
g.id AS groupId,
g.group_name AS groupName,
g.category AS category,
g.project_type AS projectType,
g.total_teams AS totalTeams,
g.total_participants AS totalParticipants,
g.display_order AS displayOrder,
d.id AS detailId,
d.venue_id AS venueId,
d.venue_name AS venueName,
d.time_slot AS timeSlot,
p.id AS participantId,
p.organization AS organization,
p.check_in_status AS checkInStatus,
p.schedule_status AS scheduleStatus,
p.performance_order AS performanceOrder
FROM
martial_schedule_group g
LEFT JOIN
martial_schedule_detail d ON g.id = d.schedule_group_id AND d.is_deleted = 0
LEFT JOIN
martial_schedule_participant p ON g.id = p.schedule_group_id AND p.is_deleted = 0
WHERE
g.competition_id = #{competitionId}
AND g.is_deleted = 0
ORDER BY
g.display_order ASC,
p.performance_order ASC
</select>
```
#### 4. 重写 Service 层方法
修改 `MartialScheduleServiceImpl.getScheduleResult()` 方法:
```java
@Override
public ScheduleResultDTO getScheduleResult(Long competitionId) {
// 使用优化的一次性JOIN查询获取所有数据
List<ScheduleGroupDetailVO> details = scheduleGroupMapper.selectScheduleGroupDetails(competitionId);
if (details.isEmpty()) {
// 返回空结果
}
// 按分组ID分组数据在内存中处理速度很快
Map<Long, List<ScheduleGroupDetailVO>> groupMap = details.stream()
.collect(Collectors.groupingBy(ScheduleGroupDetailVO::getGroupId));
// 组装 DTO 返回
// ...
}
```
## 优化效果
### 数据库查询次数对比
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| SQL 查询次数 | 3 次 | **1 次** | **减少 66.7%** |
| 网络往返次数 | 3 次 | **1 次** | **减少 66.7%** |
| 查询优化 | 分散优化 | **统一优化** | 数据库可进行整体优化 |
### 性能提升分析
1. **减少网络开销**
- 从 3 次<><E6ACA1>络往返减少到 1 次
- 减少了 TCP 连接的建立和等待时间
- 降低了网络延迟的累积效应
2. **数据库查询优化**
- 数据库可以对整个 JOIN 查询进行统一的执行计划优化
- 可以利用索引加速 JOIN 操作
- 减少了查询解析和编译的次数
3. **数据传输优化**
- 虽然单次传输数据量可能略大,但总体网络 IO 更少
- 减少了协议头、认证等额外开销
4. **应用层优化**
- 使用 Java Stream API 在内存中快速分组
- 内存操作速度远快于网络 IO
### 预估性能提升
假设场景:
- 一个比赛有 20 个分组
- 平均每个分组有 30 个参赛者
- 单次数据库查询平均耗时 50ms
**优化前**
- 3 次查询 × 50ms = 150ms
- 加上网络延迟和 Java 处理 ≈ **200ms**
**优化后**
- 1 次查询 × 80ms = 80msJOIN 查询稍慢)
- 加上 Java 内存分组 ≈ **100ms**
**性能提升**:约 **50%** 的响应时间减少
## 实际应用建议
### 何时使用这种优化
**适用场景**
- 需要同时查询多个关联表的数据
- 数据量不是特别大(几千到几万条)
- 需要减少网络往返次数
- 关联关系明确JOIN 条件简单
⚠️ **不适用场景**
- 单表数据量超过 10 万条
- JOIN 会产生笛卡尔积爆炸
- 某些关联数据可选加载(懒加载更合适)
### 进一步优化建议
如果数据量继续增大,可以考虑:
1. **分页加载**
- 前端使用虚拟滚动或分页
- 后端添加 LIMIT/OFFSET
2. **缓存优化**
- 将常用的编排结果缓存到 Redis
- 设置合理的过期时间
3. **数据库索引**
- 确保 `competition_id`, `schedule_group_id` 有索引
- 考虑添加联合索引加速 JOIN
4. **读写分离**
- 查询走从库,减轻主库压力
- 使用 MyBatis Plus 的多数据源配置
## 相关文件
### 新增文件
- `src/main/java/org/springblade/modules/martial/pojo/vo/ScheduleGroupDetailVO.java`
### 修改文件
- `src/main/java/org/springblade/modules/martial/mapper/MartialScheduleGroupMapper.java`
- `src/main/java/org/springblade/modules/martial/mapper/MartialScheduleGroupMapper.xml`
- `src/main/java/org/springblade/modules/martial/service/impl/MartialScheduleServiceImpl.java`
## 测试验证
### 如何测试
1. **启动后端服务**
```bash
cd martial-master
mvn spring-boot:run
```
2. **访问编排页面**
```
http://localhost:2888/api/martial/project/detail?id=200
```
3. **查看数据库日志**
- 在 `application.yml` 中开启 SQL 日志
- 观察只执行了 1 次 JOIN 查询
4. **性能对比**
- 使用浏览器开发者工具查看网络请求时间
- 对比优化前后的响应时间
### 预期结果
- 后端日志中只看到 1 条 SQL 查询语句
- 页面加载速度明显提升
- 数据显示正确,功能无异常
## 总结
这次优化通过将 **3 次独立查询合并为 1 次 JOIN 查询**,显著减少了数据库往返次数和网络 IO预计可将页面加载时间减少约 50%。这是一种常见且有效的性能优化手段,特别适合需要关联多个表的查询场景。