作者: 侨歌
来源: 有赞技术团队原文
原文日期: 2021 年 9 月 3 日
有赞数据报表中心向商家提供多维度、多渠道、多周期的数据,帮助商家运营店铺并作出分析决策。维度覆盖正向和逆向交易、商品、客户、流量与营销活动;渠道包括全渠道、H5、App 和小程序;周期包括实时、自然日、自然周、自然月、近 7 天、近 30 天、季度和自定义区间。
这类数据会影响商家的运营策略,因此准确性和及时性都很重要。除了功能发布可能引入问题,每日调度中的依赖表缺失、组件异常或数据异常也会造成数据问题。线上监控的目标,是尽早发现并拦截这些问题。原文介绍了离线批处理和实时流处理的监控实践:当时商家数据大多在早上 7 点前产出,所以许多规则从 7 点开始调度;为更早发现问题,也开始在业务层表(Kylin)构建完成后触发监控。
数据从哪里来
有赞的数据主要来自交易、商品、客户等业务方以及面向消费者的埋点日志。处理阶段同时提供离线与实时数据,应用层既有通用数据报表,也有定制业务数据。原文将离线数据、实时数据、应用服务放在一条链路中考虑,强调要根据每层数据的特征安排对应规则与执行时机。
离线数据:准确性与产出及时性
离线数据指昨天及以前时间范围内的统计数据,主要通过 Hive SQL 处理和聚合,时间粒度包括自然日、自然周、自然月、近 7 天、近 30 天、季度和自定义区间。原文把离线准确性监控分成指标、表和应用三个层次。
准确性规则
指标层包含三类判断:
- 跨表对比: 比较不同表中含义相同的指标。有赞数据中心当时仍有一些页面上的同名指标由不同底层表提供;随着底层表统一模型建设,这类比较的比重预计会逐步下降。
- 同表逻辑判断: 检查同一张表中不同指标之间应满足的逻辑关系,例如支付人数不应大于支付订单数。
- 指标自身判断: 对指标本身检查枚举值、唯一性、非空性等规则。
表层包括全表或分区表的行数和数据量大小检查:既可以与过去某个时间点作同比、环比变化比较,也可以校验是否落在指定范围内。
应用层检查历史数据回归:在指标定义没有变化的前提下,对过去某天、某周或某月重复调用接口,确认其返回值没有变化。
这些规则需要结合数据链路来确定触发点。离线处理链路较长,每层的数据特征不同,不能把一种判断简单地安排在所有层统一执行。
产出及时性与触发时间
影响离线数据产出时间的上层因素主要有四项:
- 开始调度时间: 作业开始进入队列的时间,不等于开始执行的时间。
- 执行时长: 从开始执行到结束的耗时,通常受作业优先级、执行引擎和 SQL 效率影响。
- deadline: 从开始调度起允许的最长执行时间。
- 规则校验时间: 表更新时触发所配置的校验规则及电话告警。原文记载当时最多执行 8 分钟,超过后便开始下游调度。
更底层的影响因素是作业开发平台和大数据组件的稳定性。基于这些因素,原文介绍了商用指标工作流优先级至少为 P3、规划数仓时间基线,以及将 deadline 与电话告警结合等保障思路。
当数据尚未产出时,应用代码可能把指标默认为 0,也可能直接返回空。实践中还用接口返回指标值大于 0 作兜底判断,并监控 deadline 告警。这个大于零的规则只适用于业务上确认此时应该有正值的指标:合法的零值需要单独建模,不能因此直接当成数据缺失。
离线监控示例
原文给出的实践包括两种准确性检查:
- 在元数据管理平台配置“同表逻辑性”规则,并在数据变更时自动触发校验。
- 在接口自动化平台配置历史数据回归用例。监控时段为 7:00 至 24:00,每 10 分钟触发一次;除验证历史返回值外,也用于观察数仓刷数和应用发布的影响。
及时性示例则是在 7 点定时调用数据接口,检查指标值是否大于 0。原文还展示了覆盖情况和规则界面截图;本文保留其文字说明,不复制或重绘截图。
实时数据:比对上下游并观察延迟
原文把实时统计口径描述为当天零时起至当前更新时间。数据按店铺、商品等维度组织,覆盖交易(正向和逆向)、流量、营销、商品等业务,使用 Flink 处理,结果写入 Druid 和 TiDB 等底层存储。
实时准确性
实时准确性校验分为两类:
- 上下游比对: 业务方的 binlog 日志进入数据侧后,明细先写入 TiDB;数据平台按指标统计规则处理,再把统计结果与底层存储中的结果进行比较。
- 昨日实时与昨日离线比对: 等昨天的实时数据完全落库后,通过接口分别取出昨日实时指标和昨日离线指标,再比较结果。
实时及时性
上层表现可能是实时指标长时间不变或变化缓慢;下层可能是 Kafka 出现积压。原文将延迟的主要影响因素归于 Flink 配置和集群资源,并说明集群及 Kafka 的监控由运维同学负责,所以没有在这篇文章中展开。
上下游重试比较与告警代码
原文中的示例每 20 分钟执行一轮上下游检查,一轮最多尝试 500 次;如果仍不相等,则在比较汇总支付订单数后决定是否告警。展示的 Druid 检查代码如下:
@Override
@Scheduled(cron="0 0/20 * * * ?")
public void teamOrderCheck() {
tidbCheck();
druidCheck();
}
public void druidCheck() {
boolean druidAlert = true;
try {
// druid
for (int i = 0; i < 500; i++) {
boolean res = checkOnceTeamOrder();
if (res) {
druidAlert = false;
break;
}
Thread.sleep(2000);
}
String druidPayCnt = getDruidPayCnt();
String detailPayCnt = getDetailPayCnt();
if (druidAlert && !druidPayCnt.equals(detailPayCnt)) {
log.warn("500次检测均不通过.");
String content =
"实时交易数据异常预警:druid 统计支付订单数:%s, 交易明细支付订单数:%s";
alertBiz.commonAlert(
String.format(content, druidPayCnt, detailPayCnt)
);
}
} catch (Exception e) {
log.warn("team order check error.");
}
}
循环中一旦某次检查函数返回相等,就结束重试,不再发出这次 Druid 检查的告警。若一直未通过,代码再比较 Druid 汇总支付订单数和明细支付订单数;只有两者仍不相等时才告警。这里的重试是在观察上下游数据逐渐一致的过程,并不构成快照级或逐条记录一致性的证明。
每次不通过后休眠 2 秒,500 次重试的名义等待时间约为 1,000 秒(16 分 40 秒),还没有计入每次查询本身的执行时间。它可能长于 20 分钟的调度间隔;若 TiDB、Druid 检查以串行方式运行,还要评估重试对任务调度、并发和超时的影响。示例的异常分支只写日志,没有展示检查器自身失效的告警处理。生产实现还需为检查任务增加可观测性,并明确失败、超时和重试的处理方式。
对于“昨日实时与昨日离线”比较,原文使用接口自动化平台,分别调用数据应用的实时指标统计接口和离线接口。由于要等待昨日离线数据落库,执行从每天早上 7 点开始,并采用每 10 分钟轮询调度。
原文报告的效果与后续规划
文章报告:2021 年上半年以来,线上监控累计预警问题 25 次以上,其中 18 个与延迟有关,1 个问题升级为故障。作者认为这些规则有助于在商家发现问题前响应和处理。该数字是作者当时的实践记录,不能据此推断其他平台或当前系统会得到相同覆盖率或效果。
作者还提出两项尚未落地的规划:
- 评估告警影响范围: 告警发生时明确影响了哪些业务,帮助客服更准确地回应商家;问题修复后也可以据此进行有针对性的回归测试。
- 建设数据质量监控大盘: 由于监控数据分散在多个平台,需要汇总各平台的监控数据,并完成指标设计、实时任务和前后端开发。
实施时的边界
这是一篇 2021 年的数据平台实践记录。规则界面、告警平台和比较函数是作者所在环境中的实现,并不是可直接照搬的完整产品。接入前应重新核对自身的数据口径、调度平台与依赖组件;对指标大于零等假设,要先区分合法零值和真正缺数;对实时重试策略,要评估计算负载、任务超时和检查器自身故障。原文给出的预警数量与截图反映的是作者的案例,不代表已经覆盖所有数据表或所有质量风险。
来源说明: 本文依据有赞技术团队指定原文整理。原文页标注作者“侨歌”,日期为 2021 年 9 月 3 日;页面未提供单独的文章转载许可说明。本文保留作者署名和原始链接。











暂无评论内容