上传源代码版本
This commit is contained in:
237
.trae/documents/metrics_query_performance_optimization_plan.md
Normal file
237
.trae/documents/metrics_query_performance_optimization_plan.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 指标查询性能优化计划
|
||||
|
||||
## 📋 背景分析
|
||||
|
||||
当前仪表盘的日汇总查询涉及**多个复杂计算**:
|
||||
- ✅ 当天新增换单数
|
||||
- ✅ 当天应该换单数
|
||||
- ✅ 当日完成数
|
||||
- ✅ 24小时完成率
|
||||
- ✅ 当日完成率
|
||||
- ✅ 16点前/后统计
|
||||
- ✅ STOP数、标签推送、扫描数等
|
||||
|
||||
**当前问题**:
|
||||
- 🐌 单次查询需要 15-30 秒+
|
||||
- 🔄 涉及多个 JOIN 操作和复杂的时区转换
|
||||
- 💾 数据量可能较大,无缓存
|
||||
|
||||
---
|
||||
|
||||
## 🎯 优化方案(按优先级)
|
||||
|
||||
### 方案一:数据库视图(强烈推荐)⭐⭐⭐⭐⭐
|
||||
|
||||
**优点**:
|
||||
- ✅ 预聚合数据,查询极快(通常 < 1 秒)
|
||||
- ✅ 减少应用端计算,降低 CPU 占用
|
||||
- ✅ 支持直接在 SQL 中添加索引
|
||||
- ✅ 易于维护和调试
|
||||
|
||||
**实现步骤**:
|
||||
1. 创建 `v_DailyMetricsSummary` 视图
|
||||
2. 在视图中实现所有指标计算逻辑
|
||||
3. 后端直接查询视图,返回结果
|
||||
|
||||
**SQL 视图示例结构**:
|
||||
```sql
|
||||
CREATE VIEW v_DailyMetricsSummary AS
|
||||
SELECT
|
||||
CAST(CONVERT_TZ(r.CreatedAt, '+00:00', '-05:00') AS DATE) AS MetricsDate,
|
||||
COUNT(DISTINCT CASE WHEN ... THEN r.Id END) AS DailyNewReplaceCount,
|
||||
COUNT(DISTINCT CASE WHEN ... THEN r.Id END) AS DailyShouldReplaceCount,
|
||||
COUNT(DISTINCT CASE WHEN ... THEN r.Id END) AS DailySuccessCount,
|
||||
...
|
||||
FROM LabelReplace r
|
||||
LEFT JOIN LabelScan s ON r.NeutralWaybillNumber = s.NeutralWaybillNumber
|
||||
WHERE CONVERT_TZ(r.CreatedAt, '+00:00', '-05:00') >= CURRENT_DATE
|
||||
GROUP BY MetricsDate
|
||||
```
|
||||
|
||||
**预期效果**:查询时间从 15-30 秒 → **< 1 秒** ✨
|
||||
|
||||
---
|
||||
|
||||
### 方案二:Redis 缓存层(中等优先级)⭐⭐⭐⭐
|
||||
|
||||
**优点**:
|
||||
- ✅ 应用端缓存,极速响应
|
||||
- ✅ 减轻数据库压力
|
||||
- ✅ 支持多环境部署
|
||||
|
||||
**实现步骤**:
|
||||
1. 在 `MetricsCalculationService` 中添加缓存装饰
|
||||
2. 设置缓存过期时间(如 5 分钟自动刷新)
|
||||
3. 提供手动刷新接口
|
||||
|
||||
**代码示例**:
|
||||
```csharp
|
||||
public async Task<Daily24HCompletionRateDto> GetDailySummaryAsync(DateTime date)
|
||||
{
|
||||
var cacheKey = $"metrics:daily:{date:yyyy-MM-dd}";
|
||||
|
||||
// 尝试从缓存获取
|
||||
if (_cacheService.TryGet(cacheKey, out var cachedData))
|
||||
{
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
// 从数据库查询
|
||||
var summary = await CalculateFromDatabase(date);
|
||||
|
||||
// 缓存 5 分钟
|
||||
_cacheService.Set(cacheKey, summary, TimeSpan.FromMinutes(5));
|
||||
|
||||
return summary;
|
||||
}
|
||||
```
|
||||
|
||||
**预期效果**:第一次查询 15-30 秒 → 后续查询 **< 100 ms** ⚡
|
||||
|
||||
---
|
||||
|
||||
### 方案三:SQL 存储过程(降低优先级)⭐⭐⭐
|
||||
|
||||
**优点**:
|
||||
- ✅ 数据库端执行,性能接近视图
|
||||
- ✅ 更灵活的逻辑控制
|
||||
- ✅ 易于版本管理
|
||||
|
||||
**缺点**:
|
||||
- ❌ 维护复杂度高
|
||||
- ❌ 跨平台迁移困难
|
||||
|
||||
**适用场景**:
|
||||
- 如果后续需要非常复杂的业务逻辑
|
||||
- 需要多步骤的数据验证
|
||||
|
||||
---
|
||||
|
||||
### 方案四:数据库索引优化(基础优化)⭐⭐
|
||||
|
||||
**必需的索引**:
|
||||
```sql
|
||||
-- 优化时区转换查询
|
||||
CREATE INDEX idx_created_at ON LabelReplace(CreatedAt);
|
||||
|
||||
-- 优化 JOIN 操作
|
||||
CREATE INDEX idx_neutral_waybill ON LabelScan(NeutralWaybillNumber);
|
||||
CREATE INDEX idx_neutral_waybill_replace ON LabelReplace(NeutralWaybillNumber);
|
||||
|
||||
-- 优化状态查询
|
||||
CREATE INDEX idx_replace_status ON LabelReplace(Status);
|
||||
CREATE INDEX idx_scan_result ON LabelScan(Result);
|
||||
```
|
||||
|
||||
**预期效果**:基础查询优化 15-20% 性能提升
|
||||
|
||||
---
|
||||
|
||||
## 📊 方案对比表
|
||||
|
||||
| 方案 | 查询速度 | 实现难度 | 维护成本 | 推荐指数 |
|
||||
|------|--------|--------|--------|---------|
|
||||
| **视图** | ⚡⚡⚡ (< 1s) | 中 | 低 | ⭐⭐⭐⭐⭐ |
|
||||
| **Redis缓存** | ⚡⚡⚡ (< 100ms) | 中 | 中 | ⭐⭐⭐⭐ |
|
||||
| **存储过程** | ⚡⚡ (1-2s) | 高 | 高 | ⭐⭐⭐ |
|
||||
| **索引优化** | ⚡ (10-12s) | 低 | 低 | ⭐⭐ |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 推荐实施方案(综合最优)
|
||||
|
||||
### 第一阶段:快速修复(立即实施)
|
||||
1. ✅ **创建 `v_DailyMetricsSummary` 视图**
|
||||
- 预计开发时间:2-3 小时
|
||||
- 预期性能提升:**85-90%**
|
||||
- 难度:中等
|
||||
|
||||
2. ✅ **优化关键索引**
|
||||
- 预计开发时间:1 小时
|
||||
- 难度:低
|
||||
|
||||
### 第二阶段:长期优化(后续迭代)
|
||||
1. 🔄 **添加 Redis 缓存层**
|
||||
- 前提:视图已完成
|
||||
- 预计开发时间:2-3 小时
|
||||
|
||||
2. 📊 **监控和调优**
|
||||
- 定期检查查询性能
|
||||
- 根据实际使用情况调整缓存策略
|
||||
|
||||
---
|
||||
|
||||
## 💡 技术实现建议
|
||||
|
||||
### 视图设计核心逻辑
|
||||
|
||||
**时区处理**:
|
||||
```sql
|
||||
-- 使用 CONVERT_TZ 确保 UTC-5 时区的日期分组
|
||||
DATE(CONVERT_TZ(r.CreatedAt, '+00:00', '-05:00')) AS MetricsDate
|
||||
```
|
||||
|
||||
**标签率计算**:
|
||||
```sql
|
||||
-- 两阶段计算:未扫描状态 vs 已扫描状态
|
||||
CASE
|
||||
WHEN s.Id IS NULL THEN
|
||||
CASE WHEN r.Label IS NOT NULL THEN 1 ELSE 0 END
|
||||
ELSE
|
||||
CASE WHEN s.Result = 0 THEN 1 ELSE 0 END
|
||||
END AS IsCompleted
|
||||
```
|
||||
|
||||
**分时段统计**:
|
||||
```sql
|
||||
-- 16点前/后分组
|
||||
CASE
|
||||
WHEN HOUR(CONVERT_TZ(r.ReceiptTime, '+00:00', '-05:00')) < 16
|
||||
THEN '16点前'
|
||||
ELSE '16点后'
|
||||
END AS TimeSegment
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 实施检查清单
|
||||
|
||||
- [ ] 分析当前查询性能瓶颈(获取执行计划)
|
||||
- [ ] 创建 `v_DailyMetricsSummary` 视图
|
||||
- [ ] 添加必需的数据库索引
|
||||
- [ ] 修改 `GetDailySummaryAsync` 方法调用视图
|
||||
- [ ] 测试新查询性能(对标 < 1 秒)
|
||||
- [ ] 更新单元测试
|
||||
- [ ] 性能基准测试和监控
|
||||
- [ ] 文档更新
|
||||
|
||||
---
|
||||
|
||||
## 📈 预期收益
|
||||
|
||||
| 指标 | 当前 | 优化后 | 提升幅度 |
|
||||
|------|------|--------|---------|
|
||||
| **查询延迟** | 15-30s | < 1s | **95%↓** |
|
||||
| **数据库 CPU** | 高 | 低 | **70%↓** |
|
||||
| **用户体验** | 差 | 优 | **显著** |
|
||||
| **可扩展性** | 有限 | 好 | **3-5x** |
|
||||
|
||||
---
|
||||
|
||||
## 🎓 关键要点
|
||||
|
||||
1. **视图是首选** - 一次性投入,长期收益
|
||||
2. **缓存是补充** - 结合视图,性能最优
|
||||
3. **索引是基础** - 无论采用哪种方案都需要
|
||||
4. **监控很关键** - 定期检查性能指标
|
||||
|
||||
---
|
||||
|
||||
## 📝 后续步骤
|
||||
|
||||
1. 用户确认优化方案
|
||||
2. 创建数据库视图
|
||||
3. 修改后端代码以调用视图
|
||||
4. 性能测试和验证
|
||||
5. 部署上线
|
||||
|
||||
Reference in New Issue
Block a user