功能和配置
本节将介绍在 ClickHouse 中使用 dbt 时可用的一些功能。
Profile.yml 配置
要使用 dbt 连接到 ClickHouse,需在 profiles.yml 文件中添加一个 profile。ClickHouse 的 profile 遵循以下语法:
模式与数据库
dbt 模型关系标识符 database.schema.table 与 ClickHouse 不兼容,因为 ClickHouse 不支持 schema。
因此我们采用简化形式 schema.table,其中 schema 实际上就是 ClickHouse 的 database。不推荐使用 default 数据库。
SET 语句警告
在许多环境中,使用 SET 语句在所有 dbt 查询之间持久化 ClickHouse 设置并不可靠,并可能导致意外失败。对于通过负载均衡器使用 HTTP 连接并将查询分发到多个节点的场景(例如 ClickHouse Cloud),这一点尤为明显;在某些情况下,这种问题在原生 ClickHouse 连接中也可能出现。因此,我们建议将所有必需的 ClickHouse 设置配置在 dbt profile 的 "custom_settings" 属性中,作为最佳实践,而不是依赖在 pre-hook 中执行 "SET" 语句(这一做法曾被偶尔建议过)。
设置 quote_columns
为避免出现警告,请务必在 dbt_project.yml 中明确设置 quote_columns 的值。更多信息请参阅有关 quote_columns 的文档。
关于 ClickHouse 集群
在使用 ClickHouse 集群时,需要考虑两点:
- 设置
cluster参数。 - 确保写入后的读取一致性,尤其是在使用多个
threads时。
集群设置
配置文件中的 cluster 参数允许 dbt-clickhouse 在 ClickHouse 集群上运行。若在配置文件中设置了 cluster,则所有模型默认都会使用 ON CLUSTER 子句进行创建——使用 Replicated 引擎的模型除外。这包括:
- 数据库创建
- 视图物化
- 表和增量物化
- Distributed 物化
Replicated 引擎不会包含 ON CLUSTER 子句,因为它们被设计为在内部管理复制。
若要针对某个特定模型不使用基于集群的创建方式,请添加 disable_on_cluster 配置:
使用非复制引擎的 table 和 incremental 物化不会受到 cluster 设置的影响(模型只会在当前连接的节点上创建)。
兼容性
如果某个模型是在没有 cluster 设置的情况下创建的,dbt-clickhouse 会识别这一情况,并在对该模型执行所有 DDL/DML 时不使用 on cluster 子句。
写后读一致性
dbt 依赖写入后读取(read-after-insert)的一致性模型。如果无法保证所有操作都发送到同一个副本,那么对于具有多个副本的 ClickHouse 集群,这种一致性模型就不兼容。在你日常使用 dbt 的过程中,可能不会遇到问题,但可以根据集群情况采用一些策略来保证这一点:
- 如果你使用的是 ClickHouse Cloud 集群,只需在 profile 的
custom_settings属性中设置select_sequential_consistency: 1。你可以在此处找到关于该设置的更多信息。 - 如果你使用的是自托管集群,请确保所有 dbt 请求都发送到同一个 ClickHouse 副本。如果其前面有负载均衡器,尝试使用
replica aware routing/sticky sessions等机制,以始终访问同一副本。在 ClickHouse Cloud 之外的集群中添加设置select_sequential_consistency = 1是不推荐的。
功能概览
通用表配置
| Option | Description | Default if any |
|---|---|---|
| engine | 在创建表时使用的表引擎(表类型) | MergeTree() |
| order_by | 由列名或任意表达式组成的元组。这可以创建一个较小的稀疏索引,用于更快速地查找数据。 | tuple() |
| partition_by | 分区是根据指定条件对表中记录进行的逻辑组合。分区键可以是基于表列的任意表达式。 | |
| sharding_key | 分片键在向分布式引擎表插入数据时决定目标服务器。分片键可以是随机值,也可以是哈希函数的输出。 | rand()) |
| primary_key | 与 order_by 类似,是 ClickHouse 的主键表达式。如果未指定,ClickHouse 将使用 order by 表达式作为主键。 | |
| unique_key | 能唯一标识行的列名元组。与增量模型配合用于更新。 | |
| settings | “TABLE” 级别设置的映射/字典,将在诸如 CREATE TABLE 的 DDL 语句中与此模型一起使用 | |
| query_settings | ClickHouse 用户级别设置的映射/字典,将在与此模型相关的 INSERT 或 DELETE 语句中使用 | |
| ttl | 与表一起使用的 TTL 表达式。TTL 表达式是一个字符串,用于为该表指定 TTL。 | |
| indexes | 要创建的数据跳过索引列表。更多信息见下文。 | |
| sql_security | 允许你指定在执行视图底层查询时使用哪个 ClickHouse 用户。SQL SECURITY 有两个合法取值:definer、invoker。 | |
| definer | 如果 sql_security 设置为 definer,则必须在 definer 子句中指定某个已存在的用户或 CURRENT_USER。 | |
| projections | 要创建的投影(projections)列表。详细信息参见关于投影。 |
关于数据跳过索引
数据跳过索引仅适用于 table 物化方式。要为表添加数据跳过索引列表,请使用 indexes 配置:
关于投影
你可以通过 projections 配置为 table 和 distributed_table 物化方式添加投影:
注意:对于分布式表,投影会应用到 _local 表,而不是分布式代理表。
支持的表引擎
实验性支持的表引擎
| 类型 | 详情 |
|---|---|
| Distributed Table | https://clickhouse.com/docs/en/engines/table-engines/special/distributed。 |
| Dictionary | https://clickhouse.com/docs/en/engines/table-engines/special/dictionary |
如果你在使用上述任一引擎时,从 dbt 连接 ClickHouse 遇到问题,请在这里提交 issue。
关于模型设置的说明
ClickHouse 有多种类型/级别的“设置(settings)”。在上面的模型配置中,其中两类是可配置的。settings 指的是在 CREATE TABLE/VIEW 这类 DDL 语句中使用的 SETTINGS 子句,因此通常是特定于某个 ClickHouse 表引擎的设置。新的
query_settings 用于在用于模型物化的 INSERT 和 DELETE 查询中添加 SETTINGS 子句(包括增量物化)。
ClickHouse 中有数百个设置,而且并不总是很清楚哪些是“表”级设置,哪些是“用户”级设置(尽管后者通常可以在 system.settings 表中查看)。通常推荐使用默认值,若要使用这些属性,应进行充分的调研和测试。
列配置
注意: 下列列配置选项要求启用并强制执行模型契约(model contracts)。
| 选项 | 描述 | 默认值(如有) |
|---|---|---|
| codec | 一个字符串,由传递给列 DDL 中 CODEC() 的参数组成。例如:codec: "Delta, ZSTD" 会被编译为 CODEC(Delta, ZSTD)。 | |
| ttl | 一个字符串,由TTL(time-to-live)表达式组成,用于在列的 DDL 中定义 TTL 规则。例如:ttl: ts + INTERVAL 1 DAY 会被编译为 TTL ts + INTERVAL 1 DAY。 |
Schema 配置示例
添加复杂类型
dbt 会通过分析用于创建模型的 SQL,自动推断每一列的数据类型。然而,在某些情况下,此过程可能无法准确确定数据类型,进而与契约中 data_type 属性指定的类型产生冲突。为了解决这一问题,我们建议在模型 SQL 中使用 CAST() 函数显式指定所需类型。例如:
功能
物化类型:view
可以将 dbt 模型创建为 ClickHouse view, 并使用以下语法进行配置:
项目文件(dbt_project.yml):
或者配置块(models/<model_name>.sql):
物化:table
可以将 dbt 模型物化为 ClickHouse 表,并使用以下语法进行配置:
项目文件(dbt_project.yml):
或者在配置块中(models/<model_name>.sql):
物化方式:增量(incremental)
每次执行 dbt 时,表模型都会被重新构建。对于较大的结果集或复杂的转换,这可能不可行且代价极高。为了解决这一问题并减少构建时间,可以将 dbt 模型创建为 ClickHouse 增量表,并使用以下语法进行配置:
在 dbt_project.yml 中定义模型:
或者在 models/<model_name>.sql 中使用配置块:
配置
针对此物化类型的特定配置如下所示:
| Option | Description | Required? |
|---|---|---|
unique_key | 唯一标识行的列名元组。有关唯一性约束的更多详细信息,请参见此处。 | 必填。如果未提供,已变更的行会被重复添加到增量表中。 |
inserts_only | 已弃用,推荐改用行为相同的 append 增量 strategy。如果对增量模型设置为 True,则增量更新会直接插入目标表,而不会创建中间表。如果设置了 inserts_only,则会忽略 incremental_strategy。 | 可选(默认:False) |
incremental_strategy | 用于增量物化的策略。支持 delete+insert、append、insert_overwrite 或 microbatch。有关各策略的更多详细信息,请参见此处。 | 可选(默认:'default') |
incremental_predicates | 需要应用于增量物化的附加条件(仅适用于 delete+insert 策略) | 可选 |
增量模型策略
dbt-clickhouse 支持三种增量模型策略。
默认(传统)策略
一直以来,ClickHouse 仅通过异步的 “mutations” 形式有限支持更新和删除操作。 为了模拟预期的 dbt 行为, dbt-clickhouse 默认会创建一个新的临时表,该表包含所有未受影响(未删除、未更改)的“旧” 记录,以及所有新增或更新的记录, 然后将此临时表与现有的增量模型关系进行交换。这是唯一一种在操作完成之前如果出现问题仍能保留原始关系的策略;但是,由于它需要对原始表进行完整拷贝,因此执行代价较高且速度较慢。
Delete+Insert 策略
ClickHouse 在 22.8 版本中新增了实验性功能 “lightweight deletes(轻量级删除)”。与 ALTER TABLE ... DELETE
操作相比,轻量级删除要快得多,因为它不需要重写 ClickHouse 的 data parts。增量策略 delete+insert
使用轻量级删除来实现
比“传统(legacy)”策略性能显著更好的增量物化。然而,使用该策略时有一些重要注意事项:
- 必须在 ClickHouse 服务器上通过设置
allow_experimental_lightweight_delete=1来启用轻量级删除,或者 必须在你的 profile 中设置use_lw_deletes=true(这将在你的 dbt 会话中启用该设置) - 轻量级删除目前已可用于生产环境,但在 23.3 之前的 ClickHouse 版本中可能存在性能或其他问题。
- 此策略直接在受影响的表/关系上操作(不会创建任何中间或临时表), 因此如果在操作过程中出现问题, 增量模型中的数据很可能会处于无效状态
- 使用轻量级删除时,dbt-clickhouse 会启用设置
allow_nondeterministic_mutations。在极少数情况下, 使用非确定性的incremental_predicates可能会导致更新/删除记录出现竞争条件(以及相关的 ClickHouse 日志消息)。 为了确保结果一致, 增量谓词应只包含对在增量物化期间不会被修改的数据的子查询。
Microbatch 策略(需要 dbt-core >= 1.9)
增量策略 microbatch 自 dbt-core 1.9 版本起提供,用于高效处理大规模
时序数据转换。在 dbt-clickhouse 中,它基于现有的 delete_insert
增量策略,通过根据 event_time 和
batch_size 模型配置,将增量拆分为预定义的时序批次。
除了处理大规模转换之外,microbatch 还提供以下能力:
有关 microbatch 的详细用法,请参考官方文档。
可用的 Microbatch 配置
| Option | Description | Default if any |
|---|---|---|
| event_time | 指示“该行发生时间”的列。对于 microbatch 模型以及所有需要被过滤的直接父模型,这是必需的。 | |
| begin | microbatch 模型的“时间起点”。这是任何初始或全量刷新构建的起始点。例如,对按日粒度的 microbatch 模型在 2024-10-01 运行且 begin = '2023-10-01 时,将处理 366 个批次(这是闰年!)外加“今天”的批次。 | |
| batch_size | 批次的粒度。支持的值包括 hour、day、month 和 year。 | |
| lookback | 在最新书签之前额外处理 X 个批次,以捕获延迟到达的记录。 | 1 |
| concurrent_batches | 覆盖 dbt 对并发(同时)运行批次的自动检测。详细内容参见配置并发批次。设置为 true 时,批次将并发(并行)运行;设置为 false 时,批次将按顺序(一个接一个)运行。 |
Append 策略
此策略替代了早期 dbt-clickhouse 版本中的 inserts_only 设置。该方式仅将新行追加到
现有关系中。
因此不会去重,也不会创建临时或中间表。如果数据中允许存在重复,
或重复已由增量查询的 WHERE 子句/过滤条件排除,这是最快的方式。
insert_overwrite 策略(实验性)
[IMPORTANT]
目前,insert_overwrite 策略在分布式物化中尚未完全可用。
执行以下步骤:
- 创建一个与增量模型关联关系具有相同结构的暂存(临时)表:
CREATE TABLE <staging> AS <target>。 - 仅将新的记录(由
SELECT产生)插入到暂存表中。 - 仅将新的分区(存在于暂存表中的分区)替换到目标表中。
此方法具有以下优点:
- 比默认策略更快,因为它不会复制整个表。
- 比其他策略更安全,因为在 INSERT 操作成功完成之前,它不会修改原始表:如果中间发生失败,原始表不会被修改。
- 实现了“分区不可变性”的数据工程最佳实践,这简化了增量和并行数据处理、回滚等操作。
该策略要求在模型配置中设置 partition_by,并会忽略模型配置中所有其他特定于策略的参数。
物化:materialized_view(实验性)
materialized_view 物化应为对现有(源)表的 SELECT。适配器会创建一个名称为模型名的目标表,
以及一个名为 <model_name>_mv 的 ClickHouse MATERIALIZED VIEW。与 PostgreSQL 不同,ClickHouse 的物化视图不是“静态的”(且
没有对应的 REFRESH 操作)。相反,它充当“插入触发器”,在向源表插入行时,会使用在视图定义中指定的 SELECT
“转换”将新行插入目标表。参见该测试文件
以获取如何使用此功能的入门示例。
ClickHouse 支持由多个物化视图向同一目标表写入记录。为了在 dbt-clickhouse 中支持这一点,你可以在模型文件中构造一个 UNION,使每个物化视图的 SQL 都被形如 --my_mv_name:begin 和 --my_mv_name:end 的注释包裹。
例如,以下内容将构建两个物化视图,它们都向模型的同一个目标表写入数据。物化视图的名称形式为 <model_name>_mv1 和 <model_name>_mv2:
重要!
当更新包含多个物化视图(MV)的模型时,尤其是在重命名某个 MV 时, dbt-clickhouse 不会自动删除旧的 MV。相反, 将会看到如下警告:
Warning - Table <previous table name> was detected with the same pattern as model name <your model name> but was not found in this run. In case it is a renamed mv that was previously part of this model, drop it manually (!!!)
数据补齐
目前,在创建物化视图(MV)时,目标表会先使用历史数据进行填充,然后才创建 MV 本身。
换句话说,dbt-clickhouse 会先创建目标表,并根据为该 MV 定义的查询预加载历史数据。只有在这一步完成后,才会创建 MV。
如果你不希望在创建 MV 时预加载历史数据,可以通过将 catch-up 配置设置为 False 来禁用此行为:
可刷新物化视图
要使用 Refreshable Materialized View, 请在 MV 模型中按需调整以下配置(所有这些配置都应在一个 refreshable 配置对象中进行设置):
| Option | Description | Required | Default Value |
|---|---|---|---|
| refresh_interval | interval 子句(必填) | Yes | |
| randomize | 随机化子句,将出现在 RANDOMIZE FOR 之后。 | ||
| append | 如果设置为 True,每次刷新会向表中插入行,而不会删除现有行。该插入不是原子的,与常规的 INSERT SELECT 一样。 | False | |
| depends_on | 可刷新物化视图的依赖项列表。请按以下格式提供依赖项:{schema}.{view_name} | ||
| depends_on_validation | 是否校验 depends_on 中提供的依赖项是否存在。如果某个依赖项未包含 schema,则在 default schema 上执行校验。 | False |
可刷新物化视图的配置示例:
限制
- 在 ClickHouse 中创建具有依赖项的可刷新的物化视图(MV)时,如果在创建时指定的依赖项不存在,ClickHouse 不会抛出错误。 相反,该可刷新 MV 会保持在未激活状态,等待依赖项被满足之后,才会开始处理更新或刷新。 这一行为是按设计实现的,但如果所需依赖项未能及时满足,可能会导致数据可用性延迟。 建议用户在创建可刷新的物化视图之前,确保所有依赖项都已正确定义且已存在。
- 截至目前,MV 与其依赖项之间并没有真正的 “dbt 链接(dbt linkage)”,因此无法保证创建顺序。
- 尚未对多个 MV 指向同一目标模型场景下的可刷新特性进行测试。
物化:dictionary(实验性)
请参阅 https://github.com/ClickHouse/dbt-clickhouse/blob/main/tests/integration/adapter/dictionary/test_dictionary.py 中的测试, 以获取如何为 ClickHouse dictionaries 实现物化的示例。
物化:distributed_table(实验性)
通过以下步骤创建 Distributed 表:
- 使用 SQL 查询创建临时视图以获取正确的结构
- 基于该视图创建空的本地表
- 基于本地表创建 Distributed 表
- 将数据插入 Distributed 表,从而在分片之间分发数据且不发生重复。
注意:
- 为了确保下游增量物化操作能够正确执行,dbt-clickhouse 查询现在会自动包含设置
insert_distributed_sync = 1。 这可能会导致某些 Distributed 表插入操作比预期更慢。
Distributed 表模型示例
自动生成的迁移
materialization: distributed_incremental(实验性)
基于与 Distributed 表相同理念的增量模型,主要难点在于要正确处理所有增量策略。
- Append 策略 只是将数据插入到 Distributed 表中。
- Delete+Insert 策略 会创建一个分布式临时表,以便在每个分片上处理所有数据。
- Default(Legacy)策略 出于相同原因会创建分布式临时表和中间表。
只会替换各分片上的本地表,因为 Distributed 表本身不存储数据。 Distributed 表仅在启用 full_refresh 模式或表结构可能发生变化时才会重新加载。
分布式增量模型示例
自动生成的迁移
快照
dbt 快照允许对可变模型随时间发生的变更进行记录。这样一来,就可以在模型上执行按时间点的查询,使分析人员能够“回到过去”查看模型之前的状态。ClickHouse 连接器支持此功能,并可通过以下语法进行配置:
在 snapshots/<model_name>.sql 中的配置块:
有关配置的更多信息,请查看 snapshot configs 参考页面。
合约与约束
仅支持精确匹配的列类型合约。例如,如果合约中列类型为 UInt32,而模型返回 UInt64 或其他整数类型,则会失败。
ClickHouse 仅 支持在整个表/模型上的 CHECK 约束。不支持主键、外键、唯一键以及列级别的 CHECK 约束。
(参见 ClickHouse 关于 primary/ORDER BY 键的文档。)
其他 ClickHouse 宏
模型物化辅助宏
包含以下宏,用于简化创建 ClickHouse 特定的表和视图:
engine_clause-- 使用engine模型配置属性来指定 ClickHouse 表引擎。dbt-clickhouse 默认使用MergeTree引擎。partition_cols-- 使用partition_by模型配置属性来指定 ClickHouse 分区键。 默认不指定分区键。order_cols-- 使用order_by模型配置来指定 ClickHouse ORDER BY/排序键。如果未指定, ClickHouse 将使用空元组 (),表将保持未排序状态。primary_key_clause-- 使用primary_key模型配置属性来指定 ClickHouse 主键。 默认情况下会设置主键,ClickHouse 将使用 ORDER BY 子句作为主键。on_cluster_clause-- 使用cluster配置文件(profile)属性,为某些 dbt 操作添加ON CLUSTER子句: 分布式物化、视图创建、数据库创建。ttl_config-- 使用ttl模型配置属性来指定 ClickHouse 表的 TTL 表达式。 默认不设置 TTL。
s3Source 辅助宏
s3source 宏简化了使用 ClickHouse S3 表函数直接从 S3 中选择 ClickHouse 数据的流程。其工作方式是
从具名配置字典(字典名称必须以 s3 结尾)中填充 S3 表函数参数。该宏会
首先在配置文件的 vars 中查找该字典,然后在模型配置中查找。该字典可以包含
用于填充 S3 表函数参数的以下任意键:
| 参数名称 | 描述 |
|---|---|
| bucket | bucket 的基础 URL,例如 https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi。如果未提供协议,则默认使用 https://。 |
| path | 用于表查询的 S3 路径,例如 /trips_4.gz。支持 S3 通配符。 |
| fmt | 被引用 S3 对象的预期 ClickHouse 输入格式(例如 TSV 或 CSVWithNames)。 |
| structure | bucket 中数据的列结构,表示为 name/datatype 对的列表,例如 ['id UInt32', 'date DateTime', 'value String']。如果未提供,ClickHouse 将自动推断结构。 |
| aws_access_key_id | S3 访问密钥 ID(access key id)。 |
| aws_secret_access_key | S3 私密访问密钥(secret key)。 |
| role_arn | 用于安全访问 S3 对象的 ClickHouseAccess IAM 角色的 ARN。更多信息请参阅此文档。 |
| compression | S3 对象使用的压缩方式。如果未提供,ClickHouse 将尝试根据文件名来识别压缩方式。 |
请参阅 S3 测试文件 以了解如何使用此 macro 的示例。
跨数据库 macro 支持
dbt-clickhouse 支持 dbt Core 中大多数跨数据库 macro,但有以下例外:
split_partSQL 函数在 ClickHouse 中是通过splitByChar函数实现的。该函数要求使用常量字符串作为“拆分”分隔符,因此用于此 macro 的delimeter参数将被解释为字符串,而不是列名。- 类似地,ClickHouse 中的
replaceSQL 函数要求old_chars和new_chars参数为常量字符串,因此在调用此 macro 时,这些参数将被解释为字符串,而不是列名。