问题

病害导出 Excel 的时候,每条病害都得查它的类型颜色、所属层位、雷达文件的里程数组……最早是”遍历病害、一条一条查库”,导出几百上千条的时候,查库次数跟着病害数线性往上涨——典型的 N+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。

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

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

协程并发各文件

分完组,各雷达文件的导出互相不依赖,用 async 并行跑,ConcurrentHashMap 收集结果:

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

效果

大批量导出(几百条病害)的耗时从”肉眼等待”降到秒级。瓶颈从查库、重复 IO 挪到了真正绘图那一步,这才算优化到点子上。

我的经验是,性能优化先揪 N+1 和重复 IO 这种”明摆着的浪费”,再谈并发。批量预查 + 分组共享上下文 + 协程并行,基本就是这类导出场景的老三样。