福安建设厅网站,4网站免费建站,佛山网站建设首页排名,上海建设杜卡森摩托车官网微批处理#xff1a;如何通过合适的增量模型为 dbt-duckdb 加速 原文地址#xff1a;https://motherduck.com/blog/microbatch-dbt-duckdb/ 作者#xff1a;Dumky de Wilde 请注意 太长不看版#xff1a;对于数据量大、基于时间、且健壮性和回填能力很重要的表#xff0…微批处理如何通过合适的增量模型为 dbt-duckdb 加速原文地址https://motherduck.com/blog/microbatch-dbt-duckdb/作者Dumky de Wilde请注意太长不看版对于数据量大、基于时间、且健壮性和回填能力很重要的表请使用微批处理。在 dbt-duckdb 中始终使用多线程但更多线程和更多内存并不总是值得。为什么我们为 dbt-duckdb 构建微批处理支持我和大家一样喜欢一个好的基准测试只要它不是“基准营销”。但基准测试并不能完全反映你生产环境的真实工作负载。它不会告诉你当所有人都在周五晚上回家时你却不得不留下来加班是种什么体验——只因为去年还是 10GB 的表现在变成了 4TB而修复其中有问题的列需要耗费永恒的时间。基准测试衡量的是单次运行。生产环境不是单次运行。它是人们发现错误、替换部分表数据、在此过程中犯错。是三个月后发现某个列计算有误需要在无需重建三年数据的情况下修复它。这就是我们为 dbt-duckdb 贡献微批处理支持的原因。dbt 在 1.9 版本中将微批处理作为增量策略引入。它不再是进行一次庞大的表更新而是基于更小的时间批次工作。更小的批次意味着你可以使用更小的计算实例、重新处理特定的时间范围并且可以在失败后恢复而无需从头开始。微批处理在墙钟时间上并不总是最快的选择。但它是可恢复的、可并行化的、并且可回填的。这可能在未来为你节省数小时的时间。或者就像我对孩子们说的老爸式玩笑慢即是顺顺即是快。DuckDB 如何存储数据行组 vs 分区要理解为什么微批处理在 DuckDB 中的行为与其他系统不同你需要了解数据是如何物理存储的。在 BigQuery 或 Spark 这类系统中数据按物理分区组织——实际上是文件夹中的独立文件。一个按日期分区的表在磁盘上可能看起来像year2024/month01/day15/。当你查询一月份的数据时引擎只读取一月份的文件夹。这就是分区剪枝效率非常高。DuckDB 的工作方式不同。数据存储在行组中。这些是大约每个 122,000 行的数据块。就像 Parquet 文件一样存在许多行组但它们不一定与你的时间边界对齐。一月份的数据可能分布在数十个行组中与十二月和二月份的数据混合在一起。而且每天的记录数也不同。起初这可能看起来比分区慢但别忘了分区的缺点是并非所有分区大小都相同。当分区很多且很小时速度反而会变慢尤其是当你还必须为每个分区遍历文件系统上的文件夹时。DuckDB 使用区域映射来过滤行组。区域映射是追踪每个组中最小/最大值的元数据。如果一个行组的最大日期是 12 月 31 日当你请求一月份数据时引擎就会跳过它。但这与分区剪枝不同。你仍然可能扫描包含混合日期数据的行组。这也会影响并行化。DuckDB 可以并行处理不同的行组但你不能同时对同一个行组进行写入。当你的批次与行组不对齐时你就会失去部分并行化的好处。例外情况如果你的数据存储在物理分区存储中例如 S3 中按日期组织的 Parquet 文件或者在 DuckLake 中那么微批处理可以利用真正的分区剪枝。这就是微批处理大放异彩的地方。比较 dbt 增量策略全量刷新、合并、删除插入 和 微批处理不同的增量策略有不同的使用场景。在深入探讨之前有两点适用于所有策略多线程几乎总是更好。单线程与多线程执行之间的差异通常比不同策略之间的差异更大。根据你的数据优化内存使用。越多并不总是越好。DuckDB 擅长将数据溢出到磁盘但有一个最佳平衡点。如果你想自己测试我专门为使用 ClickBench 数据的 dbt 整理了一个基准测试项目dbt-duckdb-clickbench。全量刷新删除表然后从头开始重建。简单可靠。DROPTABLEtarget;CREATETABLEtargetASSELECT*FROMsource;在 DuckDB 中这通常是单次运行最快的选项。引擎针对批量操作进行了优化并且没有检查已存在数据的开销。线程数内存运行时间88GB28秒38GB31秒116GB146秒18GB148秒问题在于你每次都重建所有数据。对于小表来说没问题。但当你的表是 4TB 而只有昨天的数据发生变化时这就不可行了。追加插入新行。没有去重没有查找。INSERTINTOtargetSELECT*FROMsourceWHERE...;速度快因为无需检查。但如果运行两次就会产生重复数据。适用于去重操作在下游进行的不可变事件流。合并基于唯一键匹配。更新现有行插入新行。MERGEINTOtargetUSINGsourceONtarget.idsource.idWHENMATCHEDTHENUPDATESET...WHENNOTMATCHEDTHENINSERT...;需要 DuckDB 1.4.0。适用于维度表——例如用户属性这类你需要更新已知实体属性的场景。删除插入删除匹配行然后插入新数据。DELETEFROMtargetWHEREdate_partition2024-01-15;INSERTINTOtargetSELECT*FROMsourceWHEREdate_partition2024-01-15;比合并更简单。对于批量更新通常更快因为你不是逐行匹配。删除操作需要查找但你可以通过 WHERE 子句缩小范围。注意已删除的行在运行CHECKPOINT之前并不会被物理移除。只有那时磁盘上的实际空间才会被回收。线程数内存运行时间38GB79秒88GB91秒116GB264秒18GB292秒微批处理删除插入但限定在时间窗口内。每个批次都是独立的。-- 对于每个批次:DELETEFROMtargetWHEREevent_time2024-01-15ANDevent_time2024-01-16;INSERTINTOtargetSELECT*FROMsourceWHEREevent_time2024-01-15ANDevent_time2024-01-16;不需要唯一键。这纯粹是基于时间的。如果你需要基于键的更新请使用合并策略。线程数内存运行时间88GB71秒38GB73秒18GB204秒批处理可以并行运行并且每个批次处理更小的数据切片。你牺牲了一些开销换来了能够重新处理特定时间范围而不影响其他部分的能力。如何在 dbt-duckdb 中配置微批处理以下是如何在 dbt-duckdb 中配置微批处理模型models:-name:events_enrichedconfig:materialized:incrementalincremental_strategy:microbatchevent_time:created_atbegin:2024-01-01batch_size:day必需配置event_time用于批次处理的时间戳列begin批次生成的开始日期batch_size粒度——hour、day、month或year当你运行dbt run时它会从begin到现在生成批次。每个批次都拥有自己限定在该时间窗口内的删除插入周期。底层工作原理dbt 根据begin、batch_size和当前时间计算批次边界。对于每个批次它在上下文中设置event_time_start和event_time_end。宏生成该窗口的 DELETE 语句然后是 INSERT 语句。使用多线程时批次并行执行——每个批次都有自己的临时表以避免冲突。源配置重要在你的源上也设置event_time。这告诉 dbt 在每个批次中包含哪些数据。sources:-name:rawtables:-name:eventsconfig:event_time:created_at运行特定批次你可以重新处理特定的时间范围而不影响表的其余部分dbt run --select events_enriched --event-time-start2024-06-01 --event-time-end2024-06-30这只处理六月份的数据——表的其余部分保持不变。常见陷阱在 DuckDB 中使用 dbt 微批处理我们在实现过程中吸取了一些惨痛教训。类型转换导致全表扫描我们最初的实现将批次边界转换为 timestampWHEREevent_time2024-01-15::timestamp这导致 DuckDB 扫描整个表而不是使用区域映射进行过滤。当类型需要转换时查询规划器无法有效地将谓词下推。修复方法不要进行类型转换。让 DuckDB 根据字面量推断类型。如果你的event_time列是DATE类型与日期字符串比较是可以的。如果是TIMESTAMP类型同理。行组与批次不对齐即使在微批处理中你在 DuckDB 中也无法获得真正的分区剪枝。你的每日批次并不映射到物理存储边界。区域映射有帮助但你仍然可能触及包含多天数据的行组。这与 BigQuery 或 Spark 不同在那些系统中分区剪枝意味着整个文件被跳过。临时表冲突在开发早期我们的临时表命名仅基于模型。在并行批次执行时多个批次试图使用同一个临时表。这很糟糕。简单修复在临时表标识符中包含批次时间戳。每个批次获得自己的工作空间。始终使用 UTCdbt 在生成批次之前将所有时间转换为 UTC。不要与它对抗。在你的event_time列中使用 UTC或者至少要知道无论源数据的时区如何批次边界都是按 UTC 计算的。选择合适的 dbt 增量策略策略何时使用全量刷新小表重建速度快需要保证一致性增量逻辑过于复杂不值得合并你有一个唯一键需要原地更新现有行维度表缓慢变化的数据删除插入替换数据块而非单行对于你的用例逻辑比合并更简单微批处理时间序列或基于事件的数据需要回填或重新处理特定时间范围希望并行批次处理从部分故障中恢复很重要物理分区的源S3、DuckLake不要使用微批处理的情况当你需要基于键的更新时使用合并当你的数据不是基于时间时或者当你纯粹为单次运行的墙钟时间进行优化时。结论为什么微批处理对生产环境 dbt 管道至关重要在我们的基准测试中微批处理并不是最快的策略。对于单次运行全表重建通常在墙钟时间上胜出。但是数据产品在其生命周期中的性能不仅仅包括执行时间。它还包括出现故障时的恢复时间、无需重建一切即可回填的能力以及当有人在三个月前的数据中发现错误时的操作简易性。我们有意将微批处理实现为删除插入而不是合并因为这对于时间序列数据更有意义。你替换的是时间窗口而不是按键更新单个记录。该实现现已集成在 dbt-duckdb 的主分支中并将在下一个版本中发布。要今天尝试uvadddbt-duckdb githttps://github.com/duckdb/dbt-duckdb资源GitHub 上的微批处理 PR #681 —— 原始实现dbt-duckdb-clickbench —— 自行测试策略的基准测试仓库dbt 微批处理文档 —— 关于微批处理的官方 dbt 文档DuckDB 增量模型 —— MotherDuck 关于 dbt 集成的文档