在电信网络优化领域,测量报告(MR)数据是评估网络覆盖质量的核心依据。然而,原始 MR 数据具有"量大、点多、面广"的特征:全省每天产生的 MR 记录可达数十亿条,且包含大量无效采样点、GPS 漂移数据和异常值。如何高效、准确地将这些海量离散数据转化为可用于分析的结构化栅格指标,是运维系统面临的最大技术挑战之一。
本文将分享如何基于 Spring Cloud Alibaba 微服务架构,结合 Spring Batch 批处理框架与 XXL-JOB 分布式调度中心,构建一个高性能、高可靠的 MR 数据清洗流水线。我们将重点探讨如何利用分片(Sharding)策略实现多线程并行处理,解决传统单线程作业耗时过长的问题,并实现断点续传与精准的事务管理
一、 业务痛点与技术选型
1. 核心痛点
- 处理效率低:传统 JDBC 循环读取方式在处理亿级数据时,内存占用高且响应极慢。
- 资源竞争严重:大规模数据写入容易引发数据库锁竞争,导致 IO 瓶颈。
- 容错能力弱:一旦任务中途失败,缺乏有效的断点续传机制,需从头重跑,浪费计算资源。
2. 技术架构
- 调度层 :XXL-JOB。负责触发批处理任务,支持分片广播、故障转移和动态 Cron 配置。
- 处理层 :Spring Batch 。提供标准化的
ItemReader、ItemProcessor、ItemWriter模型,支持流式读取和块(Chunk)提交。 - 存储层 :PostgreSQL + PostGIS。利用其强大的空间数据处理能力存储清洗后的栅格结果。
- 治理层 :Nacos + Sentinel。管理配置并监控批处理任务的执行状态与流量。
二、 Spring Batch 核心组件设计
针对GridMrInfo(栅格 MR 信息)和 MROGridNearL(邻区关系)等实体,我们设计了如下批处理流程:
1. 分片读取器 (Sharding ItemReader)
为了充分利用多核 CPU,我们将全量数据按 cell_id 或 geohash 进行逻辑分片。每个分片由一个独立的线程处理。
java
@Component
public class MrDataPartitioner implements Partitioner {
@Override
public Map<String, ExecutionContext> partition(int gridSize) {
Map<String, ExecutionContext> partitions = new HashMap<>();
// 假设按小区 ID 哈希分片
for (int i = 0; i < gridSize; i++) {
ExecutionContext context = new ExecutionContext();
context.putInt("partitionIndex", i);
context.putInt("gridSize", gridSize);
partitions.put("partition" + i, context);
}
return partitions;
}
}
2. 定制化处理器 (ItemProcessor)
在 Processor 中执行复杂的业务逻辑:过滤无效点、GeoHash 编码转换、RSRP/SINR 异常值剔除。
java
@Component
@StepScope // 确保每个 Step 实例独立
public class MrDataProcessor implements ItemProcessor<RawMrRecord, GridMrInfo> {
@Value("#{stepExecutionContext['partitionIndex']}")
private int partitionIndex;
@Override
public GridMrInfo process(RawMrRecord item) throws Exception {
// 1. 基础校验
if (item.getRsrp() == null || item.getLatitude() == null) {
return null; // 返回 null 则跳过该条记录
}
// 2. 异常值过滤
if (item.getRsrp() < -140 || item.getRsrp() > -40) {
return null;
}
// 3. 空间索引构建
String geohash = GeoHashUtil.encode(item.getLatitude(), item.getLongitude(), 7);
// 4. 组装目标实体
GridMrInfo gridInfo = new GridMrInfo();
gridInfo.setGeohash(geohash);
gridInfo.setRsrp(item.getRsrp());
gridInfo.setSinr(item.getSinr());
gridInfo.setCellId(item.getCellId());
return gridInfo;
}
}
3. 批量写入器 (JdbcBatchItemWriter)
采用批量插入策略,减少数据库交互次数。
java
@Bean
public JdbcBatchItemWriter<GridMrInfo> writer(DataSource dataSource) {
JdbcBatchItemWriter<GridMrInfo> writer = new JdbcBatchItemWriter<>();
writer.setDataSource(dataSource);
writer.setSql("INSERT INTO grid_mr_info (geohash, rsrp, sinr, cell_id) VALUES (:geohash, :rsrp, :sinr, :cellId)");
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
return writer;
}
三、 XXL-JOB 分布式调度集成
通过 XXL-JOB 的分片广播模式,我们可以将一个大任务拆解为多个子任务,分发到集群中的不同节点执行。
1. 任务配置
- 路由策略:分片广播。
- Cron 表达式 :
0 0 2 * * ?(每日凌晨 2 点执行)。 - 阻塞处理策略:单机串行。
2. 执行器代码实现
java
@Component
public class MrCleanJobHandler {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job mrCleanJob;
@XxlJob("mrCleanJobHandler")
public ReturnT<String> execute(String param) throws Exception {
// 获取 XXL-JOB 分片参数
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
// 启动 Spring Batch 任务,传入分片上下文
JobParameters params = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.addInt("shardIndex", shardIndex)
.addInt("shardTotal", shardTotal)
.toJobParameters();
jobLauncher.run(mrCleanJob, params);
return ReturnT.SUCCESS;
}
}
四、 性能优化与稳定性保障
1. 事务管理与断点续传
Spring Batch 默认以 Chunk 为单位提交事务。我们设置 commit-interval=1000,即每处理 1000 条记录提交一次。若任务在第 5000 条失败,重启后会自动从第 4001 条开始继续处理(依赖 BATCH_STEP_EXECUTION 表的状态记录)。
2. 数据库连接池调优
由于批处理属于 IO 密集型操作,需适当增大 HikariCP 的连接数,并开启 rewriteBatchedStatements=true 以启用 MySQL/PG 的批量重写功能,显著提升写入速度。
3. Sentinel 流控保护
在 Processor 中集成 Sentinel,防止因某批次数据异常复杂导致 CPU 飙升,影响同一节点上其他微服务的正常运行。
java
@SentinelResource(value = "mrProcessBlock", blockHandler = "handleBlock")
public GridMrInfo process(RawMrRecord item) {
// ... 业务逻辑
}
五、 前端可视化监控 (Vue 3)
为了方便运维人员实时掌握清洗进度,我们开发了一个基于 Vue 3 的监控面板。
- 实时进度条:通过 WebSocket 订阅 XXL-JOB 的执行日志,展示当前处理的记录数和百分比。
- 异常告警:当连续出现解析错误率超过阈值时,前端弹出红色警示并建议人工介入。
- 历史趋势图:利用 ECharts 展示近 7 天的数据清洗量及耗时对比。
六、 总结
通过引入 Spring Batch 与 XXL-JOB,我们成功构建了电信级的 MR 数据清洗引擎:
- 高效并行:分片策略将原本需要 10 小时的任务缩短至 1 小时内。
- 稳定可靠:完善的断点续传机制确保了在服务器重启或网络波动下的数据不丢失、不重复。
- 灵活调度:分布式调度中心实现了任务的统一管控与弹性伸缩。
这套方案不仅解决了海量数据处理难题,也为后续的 5G 栅格化覆盖评估、重叠覆盖识别等高级算法提供了高质量的数据基石。