本文将为您介绍三种实时聚合的方案,也是技术递进的过程,适用于不同的场景。
业务背景与技术挑战
传统的实时数据聚合系统在应对实际业务场景时,面临着多重复杂挑战。
晚到数据处理问题: 在分布式实时处理场景中,受网络延迟、系统抖动或上游波动影响,数据乱序到达属常态。若未对迟到数据设计合理处理机制(如 Watermark、状态回溯或重聚合),将导致历史聚合结果被错误覆盖,引发统计偏差,直接影响监控指标的准确性与业务决策的可靠性。
状态管理的复杂性: 传统有状态聚合在维度复杂或数据倾斜场景下,易导致状态规模指数级增长。不仅大幅占用内存资源,亦显著拖慢 Checkpoint 速度,增加恢复时间与失败风险,最终影响系统整体稳定性。
资源消耗与性能平衡: 实时聚合需在计算开销、存储压力与结果准确性之间取得平衡。过度依赖内存状态将推高资源成本;频繁外部存储读写则易形成 I/O 瓶颈,影响吞吐与延迟。
方案对比与适用场景分析
方案类型 | 核心优势 | 典型适用场景 | 开发复杂度 | 运维复杂度 | 数据准确性 | 对下游存储压力 | 资源效率 |
有状态聚合 | 实现简单,使用标准流处理SQL;数据时效性高 | 实时告警系统、高频交易监控系统、数据量和维度相对固定且晚到数据比例极低的场景。 | 低 | 中高(状态管理) | 较低(晚到数据) | 高 | 中等 |
无状态增量聚合 | 优秀处理晚到数据;避免流处理引擎状态管理复杂性 | 对数据准确性要求极高、晚到数据普遍存在的场景,例如财务监控指标、用户行为分析、需要精确统计的历史数据修正。 | 中等 | 较低 | 很高 | 中高(需读取历史状态) | 中等 |
数据湖中间层聚合 | 大规模数据处理能力强;显著减少下游存储系统压力;支持多版本 | 日处理量在千万级以上、数据源多样化、需要多路消费、或需将核心在线存储与分析型存储解耦的场景。对数据湖技术栈有一定要求,但可提供长期可扩展性和更优资源效率。 | 高 | 中高(组件维护) | 很高 | 低(批量写入) | 最优 |
有状态聚合
利用流处理引擎内置的状态管理机制,在内存中维护聚合状态,实现数据的实时汇聚。
对实时日志流(view_source)进行聚合,按时间(ts)和集群(cluster)统计访问量(pv)和点击量(click),并将结果写入目标表(sink_table)。
INSERT INTO sink_table
SELECT
ts, cluster,
SUM(pv) as pv,
SUM(click) as click
FROM view_source
GROUP BY ts, cluster;
方案描述
这是实时聚合的入门级方案,实现相对直接。它充分利用流处理引擎提供的标准有状态聚合能力,通过类SQL的聚合语法即可完成大多数聚合任务。当数据流经处理算子时,流处理引擎会根据预定义的聚合键查找并更新对应的状态,状态更新成功后将聚合结果写入到下游存储。。
方案分析
该方案的核心优势在于其实现门槛低和数据时效性高,特别适用于对数据处理延迟要求极高的场景。然而,其局限性也显而易见:
晚到数据处理: 若09:30的聚合结果(pv=999)因State TTL过期被清理,后续该时刻的晚到数据(pv=1)会因历史状态缺失而触发一次新的计算,导致结果被错误地覆盖为pv=1。
状态膨胀与性能: 在高并发、高基数的场景下,聚合状态可能产生热点Key,导致急剧膨胀,占用大量内存,并显著拖慢Checkpoint的执行速度,影响系统稳定性与恢复时长。
容错与恢复: 系统故障恢复时,需要从检查点加载全部状态,状态越大,恢复时间越长,影响系统可用性。
无状态增量聚合
将复杂的状态管理下推至存储层,流处理引擎仅负责无状态的增量计算,并通过与存储层的协同实现最终聚合。
无状态聚合UDAF示例,仅对当前批次或窗口内的数据进行增量计算。
public class LongSumAggUDAF extends AggregateFunction<Long, LongAccumulator> {
@Override
public LongAccumulator createAccumulator() {
return new LongAccumulator();
}
public void accumulate(LongAccumulator acc, Long value) {
acc.add(value); // 仅累加当前微批数据
}
@Override
public Long getValue(LongAccumulator acc) {
return acc.getValue();
}
}
方案描述
自定义增量聚合函数(UDAF): 不同于标准聚合函数,自定义UDAF在流处理算子层面仅对当前批次或窗口内的数据进行增量计算,而不维护跨批次的历史状态。例如,
LongSumAggUDAF
每次只累加当前流入的数值,输出的也是该批次的增量和。存储层增量回写机制: 配合自定义UDAF,在向存储写入数据之前,连接器会首先读取存储中对应主键的历史值,然后将当前批次的增量结果与历史值进行累加,最后将新的全量聚合结果写回存储。这种“读取-计算-写入”的模式确保了即使流处理任务重启或存在晚到数据,也能正确地累加到对应的时间点。
方案分析
此方案的最大优势在于彻底解决了晚到数据处理问题,任何时间到达的数据都能被正确累加,避免了因状态过期导致的数据覆盖。同时,由于流处理引擎不再维护大量状态,其内存使用和检查点性能得到了显著提升。缺点是每次写入都需要先读取存储,会增加存储系统的读取QPS和一定的处理延迟。
数据湖中间层聚合
引入了聚合数据湖(如Paimon)作为中间层,形成了更为优化的三层架构:原始数据 -> 流处理引擎(无状态聚合)-> 聚合数据湖 -> 下游存储。
Paimon聚合表示例。当主键相同时,pv列和click列会聚合结果(sum)。
-- 创建Paimon聚合表
CREATE TABLE paimon_agg (
ts TIMESTAMP(3),
cluster STRING,
pv BIGINT,
click BIGINT,
PRIMARY KEY (ts, cluster) NOT ENFORCED
) WITH (
'merge-engine' = 'aggregation',
'fields.pv.aggregate-function' = 'sum',
'fields.click.aggregate-function' = 'sum'
);
方案描述
该方案的关键在于利用数据湖的数据合并机制,在数据湖表层面实现聚合逻辑。通过配置 'merge-engine' = 'aggregation'
和指定聚合字段的聚合函数,数据湖在接收到新数据时,会自动与历史数据进行合并聚合。
流处理任务:只需将经过初步无状态聚合的增量数据写入数据湖表。
数据湖:负责在存储层面进行高效的增量聚合和版本管理。
数据湖的数据合并机制:将最终聚合结果批量、周期性地同步到下游核心存储系统。
方案分析
降低核心存储压力: 批量同步机制大大减少了对核心存储系统的实时写入QPS,提升了其稳定性。
简化流处理任务逻辑: 流处理任务不再需要复杂的存储层回读和计算逻辑,仅关注数据流入和初步增量处理。
提高系统灵活性和可维护性: 各组件职责更加清晰,数据湖作为通用层,可以支持多种下游存储系统和查询引擎。
支持多版本和历史回溯:数据湖的特性使得数据版本管理和历史快照查询成为可能。
方案选择与成本考量
方案的选择涉及多种权衡:
有状态聚合方案实现简单,时效性高,但难以处理晚到数据且状态管理复杂。
无状态聚合方案能有效处理晚到数据,运维成本低,适用性广,但需要自定义开发。
引入中间层可应对海量数据,降低下游系统负载,然其架构更重,技术要求更高。
开发成本与运维成本通常呈反向关系。最终选择必须基于业务需求、数据规模与技术能力,在成本、效率和复杂度之间找到平衡。