ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Apache Druid 数据 Rollup(预聚合)完全指南:配置、原理与最佳实践

Apache Druid 数据 Rollup(预聚合)完全指南:配置、原理与最佳实践 数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载导读Rollup滚动聚合是 Apache Druid 在**摄入阶段ingestion time**对原始数据进行的一种摘要化、预聚合处理它可以把大量原始事件压缩成显著更小的存储形态是 Druid 在高性能实时分析场景下控制存储成本与提升查询性能的核心手段。本文基于 Druid 官方文档 rollup 指南 展开结合 ingestion-spec、schema-model 与仓库中的GranularitySpec源码实现系统讲解 rollup 的配置方法、工作原理、适用场景、压缩率评估与 Perfect/Best-effort 两种模式的差异并给出可直接落地的 schema 设计建议。读完本文你将掌握如何通过granularitySpec的rollup开关控制预聚合行为、如何评估并最大化 rollup 比率、以及在不同摄入方式下如何选择正确的 rollup 模式。什么是 Rollup摄入期的摘要化预聚合Druid 可以在摄入时对数据进行 rollup从而减少磁盘上需要存储的原始数据量。Rollup 本质上是一种摘要化summarization或预聚合pre-aggregation操作。开启 rollup 后数据规模可以被显著压缩行数甚至可能降低若干个数量级orders of magnitude。作为 rollup 高效性的代价你会失去查询单个原始事件individual events的能力——原始记录一旦被聚合就再也无法按事件级别还原。因此在决定是否开启 rollup 之前需要先回答一个问题你的查询是否依赖逐行row-level的原始数据Rollup 的核心工作机制在摄入时rollup 行为由dataSchema→granularitySpec中的rollup设置控制且默认开启true。开启后Druid 会把满足以下两个条件的多行输入数据合并为一行维度dimension值完全相同——参见 schema-model 中的 dimensions 说明主时间戳primary timestamp在经过queryGranularity截断后相同——参见 ingestion-spec 的 granularitySpec 章节与 schema-model 中的 primary timestamp 说明。被合并到同一行的各行的指标metric值会按照metricsSpec中定义的聚合函数如sum、count、longSum等逐列聚合得到最终的指标值。这就是为什么 rollup 属于“预聚合”聚合发生在数据写入磁盘之前而不是查询时。关闭 Rollup 的含义当你把rollup设为false时Druid原样加载每一行不进行任何形式的预聚合。这种模式与不支持 rollup 特性的传统数据库行为类似。如果希望 Druid 将每条记录按原样存储不经过任何摘要化处理就应设置rollup: false。需要特别注意即使queryGranularity设置为none时间戳不做任何截断rollup 依然生效——只要两条记录的时间戳完全相同、维度值完全相同它们仍会被合并。这一点在 ingestion-spec 的 granularitySpec 字段说明中也有明确说明。如何配置 RollupgranularitySpec 全解Rollup 开关位于摄入规范ingestion spec的dataSchema→granularitySpec。下面是一份完整的granularitySpec示例出自 ingestion-specgranularitySpec: { segmentGranularity: day, queryGranularity: none, intervals: [ 2013-08-31/2013-09-01 ], rollup: true }granularitySpec负责四项工作通过segmentGranularity将数据源划分成时间块time chunks通过queryGranularity对时间戳做截断如需要通过intervals指定批量摄入要创建的 segment 时间块通过rollup指定是否启用摄入期 rollup。除rollup外其余三项操作都基于主时间戳。各字段说明如下摘自 ingestion-spec 的 granularitySpec 表格字段说明默认值typegranularitySpec 的类型uniformsegmentGranularity数据源的时间块粒度。同一时间块内可以创建多个 segment。例如设为day时同一天的事件落入同一时间块并可基于其他配置与输入大小进一步分区。可使用任意粒度granularity。注意同一时间块内的所有 segment 应具有相同的 segment 粒度。避免用WEEK做数据分区因为周与月、年对不齐难以按更粗粒度重新分区建议使用DAY或MONTHdayqueryGranularitysegment 内时间戳的存储分辨率必须等于或细于segmentGranularity。这是你能得到合理查询结果的最细粒度但你可以按任何更粗的粒度查询。例如设为minute时记录按分钟粒度存储可以按分钟、5 分钟、小时等任意分钟的倍数合理查询。可使用任意粒度。设为none表示时间戳不做截断原样存储。注意即使queryGranularity为nonerollup 依然生效——数据只要时间戳完全相同就会被 rollupnonerollup是否使用摄入期 rollup。即使queryGranularity为nonerollup 依然有效——时间戳完全相同的行会被合并trueintervals定义 segment 时间块的 ISO8601 区间列表如[2021-12-06T21:27:1000:00/2021-12-07T00:00:0000:00]。省略时间部分时默认按00:00:00处理。Druid 会基于segmentGranularity对区间列表进行拆分与取整。若为null或未提供批量摄入任务通常根据输入数据中的时间戳自行决定输出哪些时间块。若指定批量摄入任务可能跳过“确定分区”阶段从而加快摄入并可能一次性申请全部锁区间外的记录会被丢弃。对任何流式摄入均忽略null源码视角rollup 如何被解析与传播从源码层面看rollup 开关最终落到GranularitySpec家族实现上。仓库中server/src/main/java/org/apache/druid/segment/indexing/granularity/目录下定义了GranularitySpec接口暴露getSegmentGranularity()、getQueryGranularity()、isRollup()等核心方法UniformGranularitySpec默认实现JSON 反序列化时直接接收segmentGranularity、queryGranularity、rollup、intervals四个字段JsonPropertyArbitraryGranularitySpec支持不同区间使用不同粒度的实现BaseGranularitySpec公共基类。在UniformGranularitySpec中rollup为null时会被视为true默认开启queryGranularity与segmentGranularity为空时分别取默认值DEFAULT_QUERY_GRANULARITY与DEFAULT_SEGMENT_GRANULARITY即none与dayintervals则被转换成IntervalsByGranularity用于按 segment 粒度切分时间桶。这与文档中“rollup 默认开启”的描述完全一致。在任务执行侧批量摄入任务基类AbstractBatchIndexTask定义了抽象的isPerfectRollup()方法见getPerfectRollup相关注释与第 290 行附近用于标识任务是否处于完美guaranteedrollup 模式并据此决定锁策略如log.info(Using timeChunk lock for perfect rollup)所示完美 rollup 模式使用 timeChunk 锁。这意味着 rollup 模式不仅影响数据形态还影响批量摄入任务的加锁与并发策略。关闭 Rollup 时 metricsSpec 的建议当rollup关闭时Druid 不做任何摄入期聚合因此 ingestion-spec 建议将metricsSpec置空——没有 rollup 就没有必要配置摄入期聚合器。不过也存在例外如果你想通过定义 metric 来生成复杂列以预计算部分近似聚合如 approximate aggregations此时即使关闭 rollup 也值得在metricsSpec中定义指标。何时开启、何时关闭 Rollup建议开启 Rollup 的场景在创建表数据源table datasource时如果同时满足以下两个条件建议开启 rollup你追求最优查询性能或存在严格的存储空间约束你不需要来自高基数维度的原始值raw values。建议关闭 Rollup 的场景反之出现以下任一情况时建议关闭 rollup你需要**逐行individual rows**的查询结果你需要对任意列执行GROUP BY或WHERE查询。后者的原因在于rollup 会丢失行级粒度被聚合掉的原始行无法再被按行过滤或分组还原。不同场景共存时的策略如果不同用例对 rollup 的需求相互冲突例如一部分查询需要明细、一部分查询只需要汇总可以为不同的表分别创建不同的 rollup 配置。这是 Druid 支持多数据源的重要原因之一明细表与汇总表各司其职。如何评估 Rollup 效果测量 Rollup 比率要量化某个数据源的 rollup 收益可以比较Druid 中的行数COUNT(*)与摄入的事件总数。后者可通过摄入时生成的count类型指标num_rows累加得到。运行如下 Druid SQL 查询即可得到 rollup 比率SELECT SUM(num_rows) / (COUNT(*) * 1.0) FROM datasource其中num_rows是摄入期生成的count类型指标。结果越大说明 rollup 带来的收益越高。关于开启 rollup 时计数如何工作可参考 schema-design 的 Counting 一节。解读若结果为 10说明平均每个存储行由 10 条原始事件聚合而来存储与扫描的行数都缩减为原来的 1/10。如何最大化 Rollup 比率Rollup 比率越高存储与查询收益越大。以下策略来自 rollup 官方文档 的 Maximizing rollup ratio 章节精简 schema 设计设计更少的维度、更低基数的维度可以获得更好的 rollup 比率。维度越少、每个维度的取值种类越少多行落入同一桶的概率越高。用 Sketches 替代高基数维度使用草图sketches如 Datasketch 系列的近似聚合避免存储高基数维度——高基数维度会显著拉低 rollup 比率。调整queryGranularity在摄入时调整queryGranularity增加多行 Druid 记录时间戳匹配的概率。例如把分钟级PT1M改为五分钟级PT5M。时间戳截断得越粗同一时间桶内的行越多rollup 空间越大。按需建立多个数据源可以可选地把同一份数据加载到多个数据源建一个“完整full”数据源关闭 rollup或开启 rollup 但保持极低的 rollup 比率再建一个“精简abbreviated”数据源维度更少、rollup 比率更高。当查询只涉及“精简”集合中的维度时使用第二个数据源可以显著降低查询耗时。通常这种方法只需很小的额外存储开销因为精简数据源往往比完整数据源小得多。针对 best-effort rollup 的补救如果使用无法保证完美 rollup 的摄入配置详见下一节可以尝试切换到能保证完美 rollup 的方案在初始摄入完成后于后台对数据进行重新索引reindex或压缩compaction。Perfect Rollup 与 Best-effort Rollup根据摄入方式的不同Druid 提供两种 rollup 模式完美 rollupGuaranteed perfect rollupDruid 在摄入时完美地聚合输入数据保证任意相同时间戳、维度值组合只在一个 segment 中出现一次。尽力而为 rollupBest-effort rollupDruid可能无法完美聚合输入数据因此多个 segment 中可能残留具有相同时间戳与维度值的行。为什么会出现 best-effort rollup通常提供 best-effort rollup 的摄入方式出于以下原因之一并行化摄入但没有洗牌shuffling步骤完美 rollup 需要把可聚合的行先分到同一分区这依赖排序/洗牌预处理省去该步骤的并行摄入无法保证跨分区的完美聚合。使用增量发布incremental publishing即在收到某个时间块的全部数据之前就先定稿并发布 segment导致理论上可 rollup 的记录被拆散到不同 segment。所有类型的流式摄入都运行在 best-effort 模式下。完美 rollup 的摄入方式则会在摄入前增加一个预处理步骤先扫描整个输入数据集来确定区间与分区方案。虽然这增加了摄入耗时但它提供了实现完美 rollup 所必需的分区信息。从源码看这一“预处理确定分区”的阶段正是批量任务可以跳过determining-partitions阶段来加速摄入的前提——在ingestion-spec的intervals字段说明中也有对应描述。各摄入方式的 rollup 行为对照以下表格摘自 rollup 文档总结了各摄入方式对 rollup 的处理摄入方式处理方式Native batchindex_parallel与index类型基于配置可为 perfect 或 best-effortSQL-based batchMSQ始终 perfectHadoop始终 perfectKafka indexing service始终 best-effortKinesis indexing service始终 best-effort实践提示如果你使用Kafka / Kinesis 流式摄入并希望获得完美的 rollup 效果不要只依赖摄入时的 best-effort rollup应定期对历史 segment 执行 compaction让后台任务把分散在多个 segment 中的可聚合行重新合并。如果你使用native batch且对聚合质量有严格要求请在任务配置中启用完美 rollup 模式对应源码中isPerfectRollup()返回 true 的分支见 AbstractBatchIndexTask。Rollup 实战演示一个可复现的完整示例为了让上面的概念落地这里复现 Rollup 教程 中的完整演示。使用SQL-based ingestionMSQ 任务引擎通过 web console 的Query视图执行。示例数据为 9 条网络流量事件字段包含timestamp、srcIP、dstIP、packets、bytes。第一步加载示例数据INSERT INTO rollup_tutorial WITH inline_data AS ( SELECT * FROM TABLE(EXTERN({ type:inline, data:{\timestamp\:\2018-01-01T01:01:35Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:20,\bytes\:9024}\n{\timestamp\:\2018-01-01T01:02:14Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:38,\bytes\:6289}\n{\timestamp\:\2018-01-01T01:01:59Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:11,\bytes\:5780}\n{\timestamp\:\2018-01-01T01:01:51Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:255,\bytes\:21133}\n{\timestamp\:\2018-01-01T01:02:29Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:377,\bytes\:359971}\n{\timestamp\:\2018-01-01T01:03:29Z\,\srcIP\:\1.1.1.1\,\dstIP\:\2.2.2.2\,\packets\:49,\bytes\:10204}\n{\timestamp\:\2018-01-02T21:33:14Z\,\srcIP\:\7.7.7.7\,\dstIP\:\8.8.8.8\,\packets\:38,\bytes\:6289}\n{\timestamp\:\2018-01-02T21:33:45Z\,\srcIP\:\7.7.7.7\,\dstIP\:\8.8.8.8\,\packets\:123,\bytes\:93999}\n{\timestamp\:\2018-01-02T21:35:45Z\,\srcIP\:\7.7.7.7\,\dstIP\:\8.8.8.8\,\packets\:12,\bytes\:2818}}, {type:json})) EXTEND (timestamp VARCHAR, srcIP VARCHAR, dstIP VARCHAR, packets BIGINT, bytes BIGINT) ) SELECT FLOOR(TIME_PARSE(timestamp) TO MINUTE) AS __time, srcIP, dstIP, SUM(bytes) AS bytes, SUM(packets) AS packets, COUNT(*) AS count FROM inline_data GROUP BY 1, 2, 3 PARTITIONED BY DAY这条语句的关键点用FLOOR(TIME_PARSE(timestamp) TO MINUTE)把时间戳向下取整到分钟对应queryGranularity: minute的效果按timestamp、srcIP、dstIP分组这三列成为维度bytes、packets作为指标按SUM聚合额外生成count指标记录每次 rollup 合并了多少条原始行。第二步查询结果SELECT * FROM rollup_tutorial返回结果|__time|srcIP|dstIP|bytes|count|packets| | -- | -- | -- | -- | -- | -- | |2018-01-01T01:01:00.000Z|1.1.1.1|2.2.2.2|35,937|3|286| |2018-01-01T01:02:00.000Z|1.1.1.1|2.2.2.2|366,260|2|415| |2018-01-01T01:03:00.000Z|1.1.1.1|2.2.2.2|10,204|1|49| |2018-01-02T21:33:00.000Z|7.7.7.7|8.8.8.8|100,288|2|161| |2018-01-02T21:35:00.000Z|7.7.7.7|8.8.8.8|2,818|1|12|原始 9 行数据被压缩为5 行。第三步逐分钟拆解 rollup 过程以2018-01-01T01:01分钟内的 3 条原始事件为例{timestamp:2018-01-01T01:01:35Z,srcIP:1.1.1.1, dstIP:2.2.2.2,packets:20,bytes:9024} {timestamp:2018-01-01T01:01:51Z,srcIP:1.1.1.1, dstIP:2.2.2.2,packets:255,bytes:21133} {timestamp:2018-01-01T01:01:59Z,srcIP:1.1.1.1, dstIP:2.2.2.2,packets:11,bytes:5780}时间戳先被FLOOR到分钟01:01:00三条记录的维度值{srcIP, dstIP}完全相同因此被合并为一行packets 2025511 286bytes 9024211335780 35937count 3即上文结果表的首行。再比如01:02分钟内的 2 条事件合并后bytes 6289359971 366260、packets 38377 415、count 2而01:03分钟只有 1 条事件无可合并对象count保持为1。可以看到rollup 的实际行为正是“截断时间戳 维度分组 指标聚合”三者的组合与 tutorial-rollup 中的演示完全一致。与其它文档的衔接关于 rollup 与计数Counting的关系以及 sketches 在降维中的作用见 schema-design。关于granularitySpec全部字段的权威说明见 ingestion-spec。关于 SQL-based ingestion 中 rollup 的概念见 MSQ 概念文档。关于压缩compaction如何修复 best-effort rollup 留下的“重复行”见 compaction 文档。关于PT5M等粒度的写法与含义见 granularities 文档。赞分享数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载相关推荐5分钟快速上手Chatbox你的终极开源AI桌面助手完全指南5分钟快速上手Chatbox你的终极开源AI桌面助手完全指南 Chatbox是一款功能强大的开源AI桌面客户端专为追求隐私安全和多模型集成的用户设计。作为一AI 应用桌面应用大模型Apache Druid Rollup 完整实战指南从 SQL 批量摄入到存储压缩的原理与最佳实践Apache Druid Rollup 完整实战指南从 SQL 批量摄入到存储压缩的原理与最佳实践 本指南以 Apache Druid 的 Rollup预聚数据库OLAP大数据后端Apache Druid DataSketches Tuple 模块实战ArrayOfDoublesSketch 聚合器与后聚合器完全指南Apache Druid DataSketches Tuple 模块实战ArrayOfDoublesSketch 聚合器与后聚合器完全指南 本篇指南以 Apa数据库OLAP大数据后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表