DBT 高级特性实战
索引
ClickZetta 支持三种索引类型,在 dbt 模型的
configconfig
里声明,建表后自动创建:
索引类型 适用场景 示例查询 bloomfilterbloomfilter
等值查询(WHERE col = 'xxx'WHERE col = 'xxx'
) WHERE order_id = 'abc123'WHERE order_id = 'abc123'
invertedinverted
全文搜索(match_allmatch_all
、match_anymatch_any
函数) WHERE match_all(status, 'completed')WHERE match_all(status, 'completed')
vectorvector
向量相似度搜索 ORDER BY cosine_distance(embedding, [...])ORDER BY cosine_distance(embedding, [...])
为什么需要索引?
ClickZetta 的数据以 Parquet 文件存储在对象存储上。没有索引时,
WHERE order_id = 'abc123'WHERE order_id = 'abc123'
需要扫描所有文件的所有行。Bloomfilter 索引在每个文件上维护一个紧凑的概率数据结构,查询时先检查索引——如果索引说"这个文件里肯定没有这个值",就直接跳过整个文件,大幅减少 I/O。
倒排索引则是为全文搜索场景设计的,它把文本分词后建立词→行的映射,
match_all(col, 'keyword')match_all(col, 'keyword')
可以直接定位到包含关键词的行,而不需要扫描全表。
向量索引用于 AI 场景,支持对高维向量做近似最近邻搜索(ANN),比暴力计算所有向量的距离快几个数量级。
Bloomfilter + 倒排索引
{{ config(
materialized='table',
indexes=[
{'type': 'bloomfilter', 'columns': ['order_id']},
{'type': 'bloomfilter', 'columns': ['customer_id']},
{'type': 'inverted', 'columns': ['status']}
]
) }}
select
order_id,
customer_id,
amount,
status,
region,
dt
from {{ ref('stg_orders') }}
每个索引是一个字典,
typetype
指定类型,
columnscolumns
指定列(目前每个索引只支持单列)。dbt 在
CREATE TABLECREATE TABLE
完成后自动执行
CREATE INDEXCREATE INDEX
语句,不需要手动操作。
向量索引
{{ config(
materialized='table',
indexes=[
{
'type': 'vector',
'columns': ['embedding'],
'distance_function': 'cosine_distance',
'scalar_type': 'f32'
}
]
) }}
select id, embedding
from {{ ref('vector_source') }}
向量索引需要额外指定
distance_functiondistance_function
(支持
cosine_distancecosine_distance
、
l2_distancel2_distance
、
dot_productdot_product
等)和
scalar_typescalar_type
(
f32f32
、
f16f16
、
b1b1
)。
Clone(零拷贝克隆)
Clone 是 ClickZetta 的零拷贝克隆能力——克隆一张表不需要复制数据,只复制元数据,创建速度极快,也不占用额外存储。
为什么零拷贝?
ClickZetta 采用存算分离架构,数据以 Parquet 文件存储在对象存储(OSS/S3/COS)上,表的元数据(哪些文件属于这张表)存储在元数据服务里。Clone 操作只是在元数据层面创建一个新的"指针",指向同一批 Parquet 文件,不需要复制任何数据文件。克隆后的表和源表共享底层文件,直到其中一方发生写入操作时才会产生新文件(Copy-on-Write)。
这意味着克隆一张 1TB 的表和克隆一张 1KB 的表耗时相同,都是毫秒级。
基本克隆
{{ config(
materialized='clone',
source=target.database ~ '.' ~ target.schema ~ '.fct_orders_partitioned'
) }}
sourcesource
指定被克隆的表,
target.database ~ '.' ~ target.schematarget.database ~ '.' ~ target.schema
是 Jinja 拼接当前连接的 database 和 schema。
典型用途 :CI/CD 环境隔离——在测试环境快速克隆生产表,测试完成后删除,不影响生产数据。
Time Travel 克隆
克隆到历史时间点,用于数据误操作恢复或历史版本对比:
{{ config(
materialized='clone',
source=target.database ~ '.' ~ target.schema ~ '.fct_orders_partitioned',
at_timestamp="current_timestamp() - interval 1 hours"
) }}
几点注意事项(来自 dbt-clickzetta examples 的注释):
时间点必须在 data_retention_daysdata_retention_days
保留周期内(最长 90 天)
源表在该时间点必须已存在
时间戳使用服务器时区(通常为 UTC+8)
常用时间表达式:
current_timestamp() - interval 1 hours -- 1 小时前
current_timestamp() - interval 1 days -- 1 天前
'2024-01-05 15:00:00' -- 固定时间点
date_sub(current_date(), 1) -- 昨天
物化视图
物化视图预计算并物理存储查询结果,适合高频固定聚合查询加速。与 Dynamic Table 的区别:物化视图需要手动刷新,Dynamic Table 自动按周期刷新。
{{ config(materialized='materialized_view') }}
select
region,
dt,
count(order_id) as order_count,
sum(amount) as revenue
from {{ ref('stg_orders') }}
where status = 'completed'
group by region, dt
选型建议 :
需要自动持续刷新 → 用 Dynamic Table
查询模式固定、可以接受手动刷新 → 用物化视图
不需要物理存储、只需逻辑封装 → 用普通视图
VCluster per-model
ClickZetta 支持为单个模型指定计算集群,实现不同类型模型的资源隔离:
{{ config(
materialized='dynamic_table',
refresh_interval='1 HOUR',
refresh_vc='default_ap' -- dynamic_table 专用:指定刷新任务使用的集群
) }}
select ...
from {{ ref('stg_orders') }}
也可以在
dbt_project.ymldbt_project.yml
里按目录批量配置(
vclustervcluster
是通用的执行集群,
refresh_vcrefresh_vc
是 dynamic_table 专用的刷新集群):
models:
my_project:
marts:
+vcluster: default_ap # 聚合查询模型用分析型集群
staging:
+vcluster: default # ETL 写入模型用通用型集群
集群类型选型 :
场景 推荐集群类型 原因 incremental 写入、seed 通用型(DEFAULTDEFAULT
) 支持自动小文件合并,适合频繁写入 大规模聚合查询、Dynamic Table 刷新 分析型(DEFAULT_APDEFAULT_AP
) 针对大规模扫描和聚合优化 数据同步、CDC 同步型(PORTAL_SYNC_VCPORTAL_SYNC_VC
) 专为数据集成场景设计
Grants
dbt 支持在模型运行后自动授权,让指定角色可以查询该表:
{{ config(
materialized='table',
grants={
'select': [var('grant_role', 'workspace_analyst')]
}
) }}
select
region,
count(order_id) as order_count,
sum(amount) as revenue
from {{ ref('stg_orders') }}
where status = 'completed'
group by region
var('grant_role', 'workspace_analyst')var('grant_role', 'workspace_analyst')
使用 dbt 变量,默认授权给
workspace_analystworkspace_analyst
角色,也可以在运行时覆盖:
dbt run --vars '{"grant_role": "my_role"}' --select regional_revenue_with_grants
⚠️ 注意 :
grantsgrants
里的角色名必须是 workspace 内已存在的角色,否则
dbt rundbt run
会报错。
组合使用示例
以下是一个综合使用多个高级特性的模型:
{{ config(
materialized='incremental',
incremental_strategy='delete+insert',
unique_key='order_id',
vcluster='default', -- ETL 写入用通用型集群
indexes=[
{'type': 'bloomfilter', 'columns': ['order_id']},
{'type': 'bloomfilter', 'columns': ['customer_id']},
{'type': 'inverted', 'columns': ['status']}
],
grants={
'select': ['workspace_analyst']
}
) }}
select
order_id,
customer_id,
amount,
status,
region,
dt,
updated_at
from {{ ref('stg_orders') }}
{% if is_incremental() %}
where updated_at > (select max(updated_at) from {{ this }})
{% endif %}
这个模型:
用 delete+insertdelete+insert
策略增量更新,在通用型集群(defaultdefault
)上运行
建表后自动创建 bloomfilter 和倒排索引
运行后自动授权给 workspace_analystworkspace_analyst
角色
相关文档