问题

病害导出 Excel 时,每个病害要查它的类型颜色、所属层位、雷达文件里程数组……最开始是“遍历病害,逐条查库”,导出几百上千条病害时,数据库查询次数跟病害数线性涨——典型的 N+1。批量导出耗时主要耗在查库上,而不是绘图。

优化

三步走:

1. 批量预查询,干掉 N+1

把循环内的单条查询改成开始时一次性批量查,构造 Map 缓存:

val colorMap = service.batchQueryColors(defects.map { it.typeId })
defects.forEach { d -> d.color = colorMap[d.typeId] } // O(1) 查

病害类型颜色这种“N 个病害只有几种类型”的数据,批量查一次从 N 次查询降为 1 次。

2. 按雷达文件分组,共享渲染上下文

原来每个病害导出截图都重新打开雷达文件、重算里程数组。改成按雷达文件分组,同一文件的病害共享一次“打开文件 + 计算里程数组”的上下文,避免重复 IO。

3. 协程并发各文件

分组后,各雷达文件的导出互相独立,用 async 并行,ConcurrentHashMap 收集结果:

coroutineScope {
defects.groupBy { it.radarFileId }
.map { (fileId, group) -> async { renderGroup(fileId, group) } }
.awaitAll()
}

效果

大批量导出(几百条病害)耗时从“明显等待”降到秒级。瓶颈从查库 / 重复 IO 转移到真正的绘图上,优化才算做到点子上。

小结

性能优化先抓 N+1 和重复 IO 这类“明显浪费”,再做并发。批量预查 + 分组共享上下文 + 协程并行,基本是这类导出场景的通用三板斧。