数据采集与预处理:把脏数据洗干净
上一章我们聊了覆盖度Q值的定义和计算逻辑。但有个残酷的现实——如果你喂给模型的是垃圾数据,那算出来的Q值再漂亮也是废话。这一章咱们就聚焦在“怎么把原始数据变成能用的干净数据”。
我个人习惯把这个阶段叫做“数据炼油”。原油从地里抽出来不能直接灌进汽车油箱,得经过蒸馏、脱硫、裂解。监控数据也一样。我当年在做一个大型电商的履约时效监控时,光是数据源就有七八个系统,时间戳格式还不一样……嗯,那段日子确实让人头秃。但预处理做扎实了,后面动态阈值的计算就稳了。
3.1 数据源选择
先说说数据源从哪来。覆盖度Q值的计算依赖两类数据:一类是生产事件流(比如告警、异常订单、故障工单),另一类是业务基线指标(比如正常订单量、并发数)。常见的数据源有这些:
- 监控系统API:Prometheus、Zabbix、阿里云监控等,直接拉指标时间序列。
- 业务数据库:MySQL、PostgreSQL里的订单表、日志表。
- 日志文件:服务器日志、中间件日志,通过ELK或Spark消费。
- 消息队列:Kafka、RocketMQ中的实时事件流。
选择数据源时要问自己三个问题:数据够实时吗?采样周期稳定吗?事件定义和Q值窗口对齐吗?我建议直接画一张数据血缘图,把每个字段的源头、口径、时间精度标清楚。后面清洗时能省很多麻烦。
3.2 数据清洗
数据源拉下来,第一件事不是算指标,而是洗数据。常见的问题我经验里排前三的是:
- 重复记录——比如同一个订单既进了MQ又落库,被重复消费。我曾因为没去重,导致某时段Q值算出来超过100%,严重误导了阈值判断。
- 缺失值——某些时间窗口没有事件上报,可能因为网络抖动。直接拿来算Q值会得到一个巨大的分母,覆盖度瞬间崩盘。
- 字段类型错乱——时间戳有的是字符串"2024-01-01 08:00:00",有的是Unix毫秒数,必须统一。
清洗流程我一般按这个顺序跑:
- 去重:按事件ID和时间戳去重,保留最晚到达的那条。
- 缺失值填充:连续缺失少于3个窗口,用线性插值;超过则标记为“数据缺失”,跳过该窗口。
- 格式转换:所有时间戳转成Unix秒级整数,方便做窗口聚合。
3.3 异常值处理
清洗完数据,下一步是揪异常值。注意:异常值不一定是脏数据,它可能是真实的突发流量(比如双11大促),但用这种极值去训练动态阈值就不太合适。我们需要区分系统异常和业务高峰。
我常用两类方法:
| 方法 | 原理 | 适用场景 |
|---|---|---|
| IQR(箱线图) | Q1 - 1.5*IQR ~ Q3 + 1.5*IQR | 数据分布非对称,有离群点 |
| Z-score | 超出均值±3σ视为异常 | 数据近似正态分布 |
| 移动窗口中位数 | 以当前窗口前后N个窗口的中位数做对比 | 趋势型时间序列(如日活) |
遇到异常值,我一般这么处理:
- 如果确认是采集故障(比如某台机器宕机导致数据全为0),直接剔除该窗口。
- 如果是真实但极端的业务高峰,我会标记但不剔除,后续在动态阈值训练时给该窗口降低权重。
- 如果无法溯源,就采用中位数替换——稳,不会像均值那样被拉偏。
3.4 数据标准化
最后一步,标准化。为什么要做?你想想看,Q值的绝对数值和系统QPS、业务规模强相关。同一个Q值0.95,在峰值时段和闲时段的意义完全不同。标准化能消除量纲影响,让动态阈值模型关注“模式”而不是“绝对值”。
常用两种方法:
| 方法 | 公式 | 特点 |
|---|---|---|
| Min-Max标准化 | (x - min) / (max - min) | 将数据映射到[0,1]区间;受极端值影响大 |
| Z-score标准化 | (x - μ) / σ | 均值为0,标准差为1;对异常值敏感 |
| 分位数标准化 | 将数据映射到目标分布的分位数 | 鲁棒性最强,适合非正态分布 |
实际项目中,我偏好使用分位数标准化。它不要求数据服从正态分布,而且对异常值没那么敏感。下面是一段标准化处理的Python代码示例:
import numpy as np
from sklearn.preprocessing import RobustScaler
# 假设 q_values 是一维数组,长度等于窗口数
q_values = np.array([0.98, 0.95, 0.87, 0.91, 0.99, 1.05])
# 注意:1.05是异常值(可能由于业务波动)
q_values = q_values.reshape(-1, 1)
scaler = RobustScaler(quantile_range=(25, 75))
q_scaled = scaler.fit_transform(q_values)
print(q_scaled)
# 输出:
# [[ 0.40824846]
# [ 0.20412415]
# [-0.40824846]
# [ 0. ]
# [ 0.61237244]
# [ 1.42886902]]
好了,数据源选好、脏数据洗掉、异常值处理完、标准化落地——现在我们的Q值序列才算真正“可训练”。这一套流程跑下来,大概能过滤掉80%的低质数据。剩下的20%?那是动态阈值模型自己的事了。
这一章的预处理工作看起来琐碎,但它是整个动态阈值监控的地基。地基没打牢,后面盖的楼越高风险越大。我个人习惯在每次新增数据源后,都重新跑一遍预处理流水线并比对前后差异——花不了10分钟,但能帮你省下后面几天的排查时间。