资讯动态

Apache Druid 数据更新实战:使用 SQL REPLACE 与 OVERWRITE 实现全量覆盖、时间区间覆盖与部分 Segment 遮蔽

发布时间:2026/9/23 10:40:38 来源:尧图企业网站定制
数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载Apache Druid 将数据以按时间分区的 segment 文件 存储segment 一旦生成便不可修改。本文以 Druid SQL 的REPLACE语句为核心完整演示如何在本地集群中用OVERWRITE ALL替换整个 datasource、用OVERWRITE WHERE只覆盖指定时间区间以及通过部分 segment 遮蔽partial segment overshadowing更新单行数据实现类似事务型数据库 UPSERT 的效果。读完本文你将能熟练使用 MSQmulti-stage query 任务引擎完成数据的更新、插入与删除并理解其底层 segment 版本与锁机制。1. 理解 Druid 的更新模型时间分区、不可变 Segment 与时间范围Druid 的更新机制与关系型数据库有本质区别理解这一点是掌握全部更新操作的前提Segment 不可变Druid 将数据按时间块分区存入 segment 文件segment 创建后其内容无法直接修改。所谓更新本质上是用新 segment 替换旧 segment或者用新 segment 遮蔽旧 segment 的局部数据。以时间范围定位数据Druid 不用主键或维度值来定位要更新的行而是用时间范围。__time时间戳之外的数据完全不受影响。这一点与事务型数据库按主键做 UPSERT 的直觉不同但正因如此Druid 才能高效地做整块时间数据的新旧切换。可执行的更新形态利用上述特性你可以用 Druid SQL 的REPLACE语句完成三类操作——整段替换更新 删除、区间内替换部分更新 插入 删除近似于事务型数据库的 UPSERT。从 数据更新总览 可以进一步确认覆盖操作的语义是用时间范围覆盖已有数据替换时间范围之外的数据不受影响并且 Druid 的原子更新机制保证查询会在时间块time chunk粒度上从旧数据无缝切换到新数据。另外要注意两点并发约束同一 datasource 的同一时间区间内摄入ingestion与覆盖overwrite不能并发执行覆盖进行时该区间的其他摄入会被排队但其他时间区间的摄入以及只读查询不受影响仍可基于旧版本数据正常进行。Druid 也不支持按主键的单条记录更新。2. 前提条件与运行环境在开始前请先完成以下准备按 本地 Quickstart 下载并启动 Druid。本地自动单机配置会默认加载druid-multi-stage-query扩展因此开箱即可使用 MSQ 任务引擎执行 SQL 语句无需额外加载数据。建议先完成 查询数据教程熟悉在 Web 控制台 的Query视图中运行 SQL 的方式。本文所有示例都在 Web 控制台的Query视图中执行由 MSQ 任务引擎以批处理任务的形式运行 SQL。3. 准备示例数据用 REPLACE EXTERN 加载数据集在 Druid SQL 中REPLACE语句既可以创建新的 datasource也可以更新已有的 datasource。下面的语句通过EXTERN函数读取一段内联 JSON 数据创建名为update_tutorial的 datasourceREPLACE INTO update_tutorial OVERWRITE ALL WITH ext AS ( SELECT * FROM TABLE( EXTERN( {type:inline,data:{\timestamp\:\2024-01-01T07:01:35Z\,\animal\:\octopus\, \number\:115}\n{\timestamp\:\2024-01-01T05:01:35Z\,\animal\:\mongoose\, \number\:737}\n{\timestamp\:\2024-01-01T06:01:35Z\,\animal\:\snake\, \number\:1234}\n{\timestamp\:\2024-01-01T01:01:35Z\,\animal\:\lion\, \number\:300}\n{\timestamp\:\2024-01-02T07:01:35Z\,\animal\:\seahorse\, \number\:115}\n{\timestamp\:\2024-01-02T05:01:35Z\,\animal\:\skunk\, \number\:737}\n{\timestamp\:\2024-01-02T06:01:35Z\,\animal\:\iguana\, \number\:1234}\n{\timestamp\:\2024-01-02T01:01:35Z\,\animal\:\opossum\, \number\:300}}, {type:json} ) ) EXTEND (timestamp VARCHAR, animal VARCHAR, number BIGINT) ) SELECT TIME_PARSE(timestamp) AS __time, animal, number FROM ext PARTITIONED BY DAY这条语句包含 MSQ SQL 摄入的几个关键要素详见 EXTERN 函数 与 REPLACE 语法EXTERN的三个 JSON 参数第一个参数是 Druid input source这里用inline类型直接内嵌数据也可以换成local、http、s3等任意输入源第二个参数是 input format{type:json}表示 JSON 格式第三个参数是可选的 row signature。本例改用EXTEND子句以 SQL 方式声明列EXTEND (timestamp VARCHAR, animal VARCHAR, number BIGINT)。TIME_PARSE(timestamp) AS __timeDruid 表必须有主时间戳列__time见 主时间戳说明这里把字符串时间解析成时间戳并映射到__time。PARTITIONED BY DAYPARTITIONED BY是INSERT/REPLACE语句的必填子句决定时间分区粒度。数据会按天切分为时间块time chunk每个时间块内含一个或多个 segment。HOUR与DAY是最常用的粒度其他可选值见 PARTITIONED BY 语法如MONTH、YEAR、ISO 8601 周期串PT1H、P1D或ALL等。运行成功后在Query视图新开一个标签页执行SELECT * FROM update_tutorial查看结果|__time|animal|number| | -- | -- | -- | |2024-01-01T01:01:35.000Z|lion| 300 | |2024-01-01T05:01:35.000Z|mongoose| 737 | |2024-01-01T06:01:35.000Z|snake| 1234 | |2024-01-01T07:01:35.000Z|octopus| 115 | |2024-01-02T01:01:35.000Z|opossum| 300 | |2024-01-02T05:01:35.000Z|skunk| 737 | |2024-01-02T06:01:35.000Z|iguana| 1234 | |2024-01-02T07:01:35.000Z|seahorse| 115 |结果包含两天共 8 条记录。在这个示例 datasource 中每一行由__time、animal、number三个字段唯一标识后续用例将围绕这些行展开更新。4. 用例一OVERWRITE ALL全量覆盖整个 DatasourceREPLACE配合OVERWRITE ALL会丢弃旧数据、用新数据整体替换整个 datasource。下面的查询把update_tutorial中全部数据的时间戳整体后移一天数据落在 2024-01-02 与 2024-01-03REPLACE INTO update_tutorial OVERWRITE ALL WITH ext AS (SELECT * FROM TABLE( EXTERN( {type:inline,data:{\timestamp\:\2024-01-02T07:01:35Z\,\animal\:\octopus\, \number\:115}\n{\timestamp\:\2024-01-02T05:01:35Z\,\animal\:\mongoose\, \number\:737}\n{\timestamp\:\2024-01-02T06:01:35Z\,\animal\:\snake\, \number\:1234}\n{\timestamp\:\2024-01-02T01:01:35Z\,\animal\:\lion\, \number\:300}\n{\timestamp\:\2024-01-03T07:01:35Z\,\animal\:\seahorse\, \number\:115}\n{\timestamp\:\2024-01-03T05:01:35Z\,\animal\:\skunk\, \number\:737}\n{\timestamp\:\2024-01-03T06:01:35Z\,\animal\:\iguana\, \number\:1234}\n{\timestamp\:\2024-01-03T01:01:35Z\,\animal\:\opossum\, \number\:300}}, {type:json} ) ) EXTEND (timestamp VARCHAR, animal VARCHAR, number BIGINT)) SELECT TIME_PARSE(timestamp) AS __time, animal, number FROM ext PARTITIONED BY DAY覆盖完成后再执行SELECT * FROM update_tutorial可以看到所有__time值都比之前晚了一天且 2024-01-01 的记录已全部消失|__time|animal|number| | -- | -- | -- | |2024-01-02T01:01:35.000Z|lion| 300 | |2024-01-02T05:01:35.000Z|mongoose| 737 | |2024-01-02T06:01:35.000Z|snake| 1234 | |2024-01-02T07:01:35.000Z|octopus| 115 | |2024-01-03T01:01:35.000Z|opossum| 300 | |2024-01-03T05:01:35.000Z|skunk| 737 | |2024-01-03T06:01:35.000Z|iguana| 1234 | |2024-01-03T07:01:35.000Z|seahorse| 115 |OVERWRITE ALL适合整表重建的场景例如全量重灌、数据订正后整体替换。注意它与INSERT的差异REPLACE生成的 segment 支持基于维度的剪枝dimension-based pruning而INSERT生成的 segment 不支持详见 Clustering。5. 用例二OVERWRITE WHERE覆盖指定时间区间REPLACE支持只覆盖 datasource 的某个时间区间这是实现部分更新 插入 删除的核心手段。一个关键约束是指定的时间区间必须与PARTITIONED BY子句中的粒度对齐——例如PARTITIONED BY DAY时覆盖区间应按整天划分。下面的查询插入一行新数据flamingo同时更新 2024-01-03 这一天内的若干行。OVERWRITE WHERE子句告诉查询只更新 2024-01-03 区间内的记录其他时间2024-01-02的数据保持原样REPLACE INTO update_tutorial OVERWRITE WHERE __time TIMESTAMP2024-01-03 00:00:00 AND __time TIMESTAMP2024-01-04 00:00:00 WITH ext AS (SELECT * FROM TABLE( EXTERN( {type:inline,data:{\timestamp\:\2024-01-03T01:01:35Z\,\animal\:\tiger\, \number\:300}\n{\timestamp\:\2024-01-03T07:01:35Z\,\animal\:\seahorse\, \number\:500}\n{\timestamp\:\2024-01-03T05:01:35Z\,\animal\:\polecat\, \number\:626}\n{\timestamp\:\2024-01-03T06:01:35Z\,\animal\:\iguana\, \number\:300}\n{\timestamp\:\2024-01-03T01:01:35Z\,\animal\:\flamingo\, \number\:999}}, {type:json} ) ) EXTEND (timestamp VARCHAR, animal VARCHAR, number BIGINT)) SELECT TIME_PARSE(timestamp) AS __time, animal, number FROM ext PARTITIONED BY DAY执行后查询结果|__time|animal|number| | -- | -- | -- | |2024-01-02T01:01:35.000Z|lion| 300 | |2024-01-02T05:01:35.000Z|mongoose| 737 | |2024-01-02T06:01:35.000Z|snake| 1234 | |2024-01-02T07:01:35.000Z|octopus| 115 | |2024-01-03T01:01:35.000Z|flamingo| 999 | |2024-01-03T01:01:35.000Z|tiger| 300 | |2024-01-03T05:01:35.000Z|polecat| 626 | |2024-01-03T06:01:35.000Z|iguana| 300 | |2024-01-03T07:01:35.000Z|seahorse| 500 |对比上一次的结果可以观察到四类变化插入新增了一行flamingonumber999。更新维度值改写原opossum行变成了tiger时间与 number 不变。更新维度值改写原skunk行变成了polecat。更新指标值改写iguana的 number 由 1234 变为 300seahorse的 number 由 115 变为 500。关于OVERWRITE WHERE的条件语法需要了解其严格约束见 REPLACE 语法条件基于__time列格式为__time [ ] TIMESTAMP多个条件之间可用AND、OR、NOT组合时间戳为闭区间包含本例为避免边界歧义采用 下限 AND 上限的半开区间写法条件中不允许出现其他表达式或函数。6. 用例三利用部分 Segment 遮蔽更新单行6.1 什么是 Segment 遮蔽OvershadowingDruid 允许用新数据覆盖某个分区内整个 segment 或 segment 的一部分这个机制称为 overshadowing。其底层规则是每个 segment 都有主版本号major version形如时间戳yyyy-MM-ddThh:mm:ss和次版本号minor version整数。segments1遮蔽s2当且仅当s1的主版本号更高或主版本号相同且次版本号更高。被遮蔽的旧 segment 不再参与查询处理从而过滤掉过期数据。常规做法是整块替换一个时间 chunk而部分遮蔽是其中较不常见但非常实用的变体在已有数据之上叠加一个时间粒度更小的 segment从而只更新极少量的行。6.2 混合粒度示例把polecat的 number 从 626 改为 486本例的要点如下查询只更新polecat这一条number记录。原 datasource 的 segment 粒度为DAY。新生成的 segment 粒度为HOUR表示的时间范围比已有数据更小。OVERWRITE WHERE与WHERE TIME_IN_INTERVAL两个子句分别指定更新的目标区间与更新数据的来源区间。由于OVERWRITE会替换区间内的全部数据若只想修改区间内的一部分数据就必须把区间内所有记录都原样携带过来、只改动目标行。这可以通过 SELECT 列表中的 CASE 函数实现本例直接构造新数据因此用CAST固定目标值。REPLACE INTO update_tutorial OVERWRITE WHERE __time TIMESTAMP2024-01-03 05:00:00 AND __time TIMESTAMP2024-01-03 06:00:00 SELECT __time, animal, CAST(486 AS BIGINT) AS number FROM update_tutorial WHERE TIME_IN_INTERVAL(__time, 2024-01-03T05:01:35Z/PT1S) PARTITIONED BY FLOOR(__time TO HOUR)这条语句与前面用例的关键差异目标区间OVERWRITE WHERE __time TIMESTAMP2024-01-03 05:00:00 AND __time TIMESTAMP2024-01-03 06:00:00把覆盖范围缩小到 2024-01-03 05:00 至 06:00 这一个小时内。数据来源FROM update_tutorial直接以原 datasource 为输入这是 reindex 的典型形态WHERE TIME_IN_INTERVAL(__time, 2024-01-03T05:01:35Z/PT1S)用 TIME_IN_INTERVAL 函数精确筛出2024-01-03T05:01:35Z这一秒/PT1S表示 1 秒的区间跨度的那一条记录。新粒度PARTITIONED BY FLOOR(__time TO HOUR)使用 FLOOR 时间函数 把输出 segment 切分到小时粒度使其时间范围小于已有的 DAY 粒度 segment从而只遮蔽局部数据。数据构造CAST(486 AS BIGINT) AS number将目标行的 number 固定为 486。若需要只改某行、其余原样保留可在此处用CASE WHEN ... THEN ... ELSE column END保留其他行的原值。执行后查询结果|__time|animal|number| | -- | -- | -- | |2024-01-02T01:01:35.000Z|lion| 300 | |2024-01-02T05:01:35.000Z|mongoose| 737 | |2024-01-02T06:01:35.000Z|snake| 1234 | |2024-01-02T07:01:35.000Z|octopus| 115 | |2024-01-03T01:01:35.000Z|flamingo| 999 | |2024-01-03T01:01:35.000Z|tiger| 300 | |2024-01-03T05:01:35.000Z|polecat| 486 | |2024-01-03T06:01:35.000Z|iguana| 300 | |2024-01-03T07:01:35.000Z|seahorse| 500 |注意polecat的 number 已从 626 变为 486其余 8 行完全不受影响。6.3 遮蔽的代价Segment 碎片化与 Compaction多次执行部分遮蔽会在同一时间 chunk 内产生粒度不一、彼此交错的多个 segment形成 segment 碎片化可能影响查询性能。Druid 官方建议用 compaction压缩 来修正碎片化compaction 任务读取某个时间区间内的一组 segment将其合并为数量更少、体积更优的新 segment从而减少逐 segment 的处理开销与查询路径上的内存开销。生产环境还可配置 自动压缩automatic compaction 让 Coordinator 在后台周期性地合并碎片。7. 覆盖操作的底层机制锁、原子性与 MSQ 执行流理解 REPLACE 的实现机制有助于在实际集群中评估并发与性能锁机制REPLACE语句会为目标 datasource 的目标时间区间获取排他写锁exclusive write lock任务运行期间该时间区间内不允许其他摄入或 compaction 操作但其他时间区间不受影响详见 MSQ 概念。更底层地Druid 的 task 使用**时间块锁time chunk lock**或segment 锁segment lock来避免并发任务生成的 segment 互相遮蔽导致查询结果错误时间块锁会锁定整个时间 chunk而 segment 锁只锁定单个 segment允许不同 segment 上的任务并发详见 tasks 中的锁定说明。原子性覆盖任务结束后新 segment 在同一时间块粒度上原子地替换旧 segment查询会从旧数据无缝切换到新数据不会出现中间状态见 数据更新总览。MSQ 执行流所有示例都经由 MSQ 任务引擎执行。流程是Broker 将 SQL 规划为原生查询并包装成query_controller任务提交给 indexing servicecontroller 按maxNumTasks等上下文参数启动若干query_worker任务worker 执行查询并生成、发布新 segment详见 Multi-stage query tasks。因此每条 REPLACE 语句运行期间至少占用两个任务槽位1 个 controller 至少 1 个 worker在估算 Middle Manager 容量时需要计入。8. 其他更新手段Reindex、Rolled-up 追加与 Lookups除了 REPLACE 覆盖数据更新总览 还介绍了另外几种实用手段可与本文用例配合使用Reindex重建索引当数据源本身就是现有数据时即REPLACE ... SELECT ... FROM table这种覆盖被称为 reindex常用于 schema 变更、重新分区、过滤无用数据、数据增强等场景。它在原子更新与锁行为上与普通覆盖完全一致。Druid 没有UPDATE或ALTER TABLE语句任何 SELECT 查询都可以在 reindex 过程中过滤、修改或增强数据。Rolled-up datasource 的追加更新对于启用 rollup 的数据源可以通过 append 追加而非重写的方式更新——当追加的行与已有行具有完全相同的维度集时查询时使用聚合算子会自动把两行合并计算之后再用 compaction 在后台把匹配的行物理合并。Lookups 映射如果某个维度值需要频繁更新应优先考虑 lookups。典型场景是把 segment 中存储的 ID 维度映射到需要周期性更新的人类可读字符串避免为改一个维度值而重写数据。9. 更多学习资源数据更新总览Druid 中数据更新机制的完整介绍Overwrite、Reindex、Rolled-up、Lookups。使用 SQL 加载外部文件生成引用外部托管数据的查询。MSQ 中 OVERWRITE 的执行细节MSQ 任务引擎如何执行 SQL REPLACE 查询。REPLACE 语法参考OVERWRITE ALL与OVERWRITE WHERE的完整语法。Compaction 指南优化 segment 大小、修正碎片化的策略。赞分享数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载相关推荐Apache Druid 数据更新实战指南Overwrite、Reindex、Rollup 更新与 Lookup 方案Apache Druid 数据更新实战指南Overwrite、Reindex、Rollup 更新与 Lookup 方案 Apache Druid 将数据按时间数据库OLAP大数据后端飞书 CLI 实战用 lark-cli markdown overwrite 覆盖更新 Drive 中的原生 Markdown 文件飞书 CLI 实战用 lark cli markdown overwrite 覆盖更新 Drive 中的原生 Markdown 文件 本文基于当前仓库中的CLIAI 技能TiXL SetTime 算子实战用 LocalTime 覆盖实现子图动画时间控制TiXL SetTime 算子实战用 LocalTime 覆盖实现子图动画时间控制 导读 SetTime 是 TiXL 算子库 Lib.numbers.ani音视频图形学桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价