@clickzetta/cz-cli-linux-x64 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cz-cli +0 -0
- package/bin/skills/clickzetta-batch-sync-pipeline/SKILL.md +386 -0
- package/bin/skills/clickzetta-cdc-sync-pipeline/SKILL.md +548 -0
- package/bin/skills/clickzetta-data-ingest-pipeline/SKILL.md +220 -0
- package/bin/skills/clickzetta-data-ingest-pipeline/eval_cases.jsonl +5 -0
- package/bin/skills/clickzetta-dynamic-table/SKILL.md +112 -0
- package/bin/skills/clickzetta-dynamic-table/best-practices/dimension-table-join-guide.md +257 -0
- package/bin/skills/clickzetta-dynamic-table/best-practices/medallion-and-stream-patterns.md +124 -0
- package/bin/skills/clickzetta-dynamic-table/best-practices/non-partitioned-merge-into-warning.md +96 -0
- package/bin/skills/clickzetta-dynamic-table/best-practices/performance-optimization.md +109 -0
- package/bin/skills/clickzetta-dynamic-table/dt-creator/SKILL.md +15 -0
- package/bin/skills/clickzetta-dynamic-table/dt-creator/references/dt-declaration-strategy.md +185 -0
- package/bin/skills/clickzetta-dynamic-table/dt-creator/references/incremental-config-reference.md +429 -0
- package/bin/skills/clickzetta-dynamic-table/dt-creator/references/refresh-history-guide.md +268 -0
- package/bin/skills/clickzetta-dynamic-table/dt-creator/references/sql-limitations.md +80 -0
- package/bin/skills/clickzetta-dynamic-table/dynamic-table-alter/SKILL.md +190 -0
- package/bin/skills/clickzetta-file-import-pipeline/SKILL.md +156 -0
- package/bin/skills/clickzetta-kafka-ingest-pipeline/SKILL.md +751 -0
- package/bin/skills/clickzetta-kafka-ingest-pipeline/eval_cases.jsonl +5 -0
- package/bin/skills/clickzetta-kafka-ingest-pipeline/references/kafka-pipe-syntax.md +324 -0
- package/bin/skills/clickzetta-oss-ingest-pipeline/SKILL.md +537 -0
- package/bin/skills/clickzetta-query-optimizer/SKILL.md +156 -0
- package/bin/skills/clickzetta-query-optimizer/references/explain.md +56 -0
- package/bin/skills/clickzetta-query-optimizer/references/hints-and-sortkey.md +78 -0
- package/bin/skills/clickzetta-query-optimizer/references/optimize.md +65 -0
- package/bin/skills/clickzetta-query-optimizer/references/result-cache.md +49 -0
- package/bin/skills/clickzetta-query-optimizer/references/show-jobs.md +42 -0
- package/bin/skills/clickzetta-realtime-sync-pipeline/SKILL.md +276 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/SKILL.md +379 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/evals/evals.json +166 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/references/dynamic-table.md +185 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/references/materialized-view.md +129 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/references/pipe.md +222 -0
- package/bin/skills/clickzetta-sql-pipeline-manager/references/table-stream.md +125 -0
- package/bin/skills/clickzetta-table-stream-pipeline/SKILL.md +206 -0
- package/bin/skills/clickzetta-vcluster-manager/SKILL.md +212 -0
- package/bin/skills/clickzetta-vcluster-manager/references/vc-cache.md +54 -0
- package/bin/skills/clickzetta-vcluster-manager/references/vcluster-ddl.md +150 -0
- package/bin/skills/clickzetta-volume-manager/SKILL.md +292 -0
- package/bin/skills/clickzetta-volume-manager/references/volume-ddl.md +199 -0
- package/bin/skills/cz-cli/SKILL.md +68 -3
- package/bin/skills/cz-cli/references/profile-setup.md +32 -0
- package/bin/skills/cz-cli-inner/SKILL.md +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Medallion 架构与 Table Stream 组合模式
|
|
2
|
+
|
|
3
|
+
## Medallion 三层管道
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Bronze(原始数据)
|
|
7
|
+
↓ Dynamic Table(清洗,INCREMENTAL)
|
|
8
|
+
Silver(清洗数据)
|
|
9
|
+
↓ Dynamic Table(聚合,FULL)
|
|
10
|
+
Gold(指标数据)
|
|
11
|
+
↓ BI 工具直接查询
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Bronze → Silver(增量清洗)
|
|
15
|
+
|
|
16
|
+
```sql
|
|
17
|
+
-- 前提:源表开启变更跟踪
|
|
18
|
+
ALTER TABLE bronze.raw_orders SET PROPERTIES ('change_tracking' = 'true');
|
|
19
|
+
|
|
20
|
+
CREATE DYNAMIC TABLE IF NOT EXISTS silver.orders_cleaned
|
|
21
|
+
REFRESH INTERVAL 15 MINUTE vcluster default
|
|
22
|
+
AS
|
|
23
|
+
SELECT
|
|
24
|
+
order_id,
|
|
25
|
+
customer_id,
|
|
26
|
+
CAST(amount AS DECIMAL(18,2)) AS amount,
|
|
27
|
+
CAST(created_at AS TIMESTAMP) AS created_at,
|
|
28
|
+
COALESCE(region, 'unknown') AS region
|
|
29
|
+
FROM bronze.raw_orders
|
|
30
|
+
WHERE order_id IS NOT NULL AND amount > 0;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Silver → Gold(聚合指标,通常 FULL)
|
|
34
|
+
|
|
35
|
+
```sql
|
|
36
|
+
CREATE DYNAMIC TABLE IF NOT EXISTS gold.orders_daily_summary
|
|
37
|
+
REFRESH INTERVAL 60 MINUTE vcluster default
|
|
38
|
+
AS
|
|
39
|
+
SELECT
|
|
40
|
+
DATE(created_at) AS stat_date,
|
|
41
|
+
region,
|
|
42
|
+
COUNT(*) AS order_count,
|
|
43
|
+
SUM(amount) AS total_revenue,
|
|
44
|
+
COUNT(DISTINCT customer_id) AS unique_customers
|
|
45
|
+
FROM silver.orders_cleaned
|
|
46
|
+
GROUP BY 1, 2;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 与 Table Stream 组合(事件驱动)
|
|
52
|
+
|
|
53
|
+
Table Stream 捕获源表变更,Dynamic Table 消费 Stream 做增量处理。
|
|
54
|
+
|
|
55
|
+
### 基本模式
|
|
56
|
+
|
|
57
|
+
```sql
|
|
58
|
+
-- 1. 源表开启变更跟踪
|
|
59
|
+
ALTER TABLE bronze.raw_orders SET PROPERTIES ('change_tracking' = 'true');
|
|
60
|
+
|
|
61
|
+
-- 2. 创建 Table Stream
|
|
62
|
+
CREATE TABLE STREAM bronze.orders_stream
|
|
63
|
+
ON TABLE bronze.raw_orders
|
|
64
|
+
WITH PROPERTIES ('TABLE_STREAM_MODE' = 'STANDARD');
|
|
65
|
+
|
|
66
|
+
-- 3. Dynamic Table 消费 Stream
|
|
67
|
+
-- 注意:Stream 作为 DT 源时,每次刷新会消费 offset
|
|
68
|
+
CREATE DYNAMIC TABLE IF NOT EXISTS silver.orders_incremental
|
|
69
|
+
REFRESH INTERVAL 5 MINUTE vcluster default
|
|
70
|
+
AS
|
|
71
|
+
SELECT order_id, customer_id, amount, status
|
|
72
|
+
FROM bronze.orders_stream
|
|
73
|
+
WHERE __change_type IN ('INSERT', 'UPDATE_AFTER');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### MERGE INTO + Table Stream(替代非分区 DT 的去重场景)
|
|
77
|
+
|
|
78
|
+
当需要按主键去重且源表持续写入时,推荐用 MERGE INTO 替代 Dynamic Table:
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
-- 1. 创建 Table Stream
|
|
82
|
+
CREATE TABLE STREAM source_stream ON TABLE source_table
|
|
83
|
+
WITH PROPERTIES ('TABLE_STREAM_MODE' = 'STANDARD', 'SHOW_INITIAL_ROWS' = 'TRUE');
|
|
84
|
+
|
|
85
|
+
-- 2. 创建目标表
|
|
86
|
+
CREATE TABLE target_table (
|
|
87
|
+
id BIGINT,
|
|
88
|
+
col1 STRING,
|
|
89
|
+
col2 INT,
|
|
90
|
+
event_time TIMESTAMP
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
-- 3. 定时调度 MERGE INTO 消费 Stream
|
|
94
|
+
MERGE INTO target_table t
|
|
95
|
+
USING (
|
|
96
|
+
SELECT id, col1, col2, event_time,
|
|
97
|
+
CASE WHEN `value` IS NULL OR `value` = '' THEN 'DELETE' ELSE 'UPSERT' END AS op
|
|
98
|
+
FROM source_stream
|
|
99
|
+
) s ON t.id = s.id
|
|
100
|
+
WHEN MATCHED AND s.op = 'UPSERT' THEN UPDATE SET
|
|
101
|
+
t.col1 = s.col1, t.col2 = s.col2, t.event_time = s.event_time
|
|
102
|
+
WHEN NOT MATCHED AND s.op = 'UPSERT' THEN INSERT
|
|
103
|
+
(id, col1, col2, event_time) VALUES (s.id, s.col1, s.col2, s.event_time);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 实时报表物化
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
-- 每小时刷新销售汇总,供 BI 工具直接查询
|
|
112
|
+
CREATE DYNAMIC TABLE IF NOT EXISTS rpt.sales_hourly
|
|
113
|
+
REFRESH INTERVAL 60 MINUTE vcluster default
|
|
114
|
+
AS
|
|
115
|
+
SELECT
|
|
116
|
+
DATE_TRUNC('hour', order_time) AS hour_bucket,
|
|
117
|
+
product_category,
|
|
118
|
+
SUM(amount) AS revenue,
|
|
119
|
+
COUNT(*) AS order_cnt,
|
|
120
|
+
AVG(amount) AS avg_order_value
|
|
121
|
+
FROM silver.orders_cleaned
|
|
122
|
+
WHERE order_time >= DATEADD(day, -30, CURRENT_DATE)
|
|
123
|
+
GROUP BY 1, 2;
|
|
124
|
+
```
|
package/bin/skills/clickzetta-dynamic-table/best-practices/non-partitioned-merge-into-warning.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# 非分区表 + 持续写入:DT 风险告警与 MERGE INTO 替代建议
|
|
2
|
+
|
|
3
|
+
## 触发条件
|
|
4
|
+
|
|
5
|
+
当用户要创建的 DT 同时满足以下条件时,**必须向用户发出告警**:
|
|
6
|
+
|
|
7
|
+
1. DT 本身是非分区表(没有 `PARTITIONED BY`,也没有 `SESSION_CONFIGS()` 引用)
|
|
8
|
+
2. 源表也是非分区表,且数据会持续写入(如 Kafka 消费落地表、CDC 明细表)
|
|
9
|
+
3. SQL 中包含按主键去重的窗口函数模式:`ROW_NUMBER() OVER (PARTITION BY key ORDER BY ts DESC) WHERE rn = 1`
|
|
10
|
+
|
|
11
|
+
## 告警内容
|
|
12
|
+
|
|
13
|
+
向用户说明以下三个风险:
|
|
14
|
+
|
|
15
|
+
### 风险 1:存储无限膨胀
|
|
16
|
+
|
|
17
|
+
非分区的 DT 和非分区的源表都没有自动的数据生命周期管理机制(`data_lifecycle` 仅对分区表生效)。随着数据持续写入:
|
|
18
|
+
- 源表数据量无限增长
|
|
19
|
+
- DT 的状态表全局维护,随数据量线性膨胀
|
|
20
|
+
- 目标表数据量同步增长
|
|
21
|
+
- 三者叠加,存储成本持续上升且不可控
|
|
22
|
+
|
|
23
|
+
### 风险 2:源表归档引发性能灾难
|
|
24
|
+
|
|
25
|
+
当存储膨胀到一定程度,运维人员通常会对源表进行归档——将历史数据迁移到冷存储或归档表,然后从源表中删除以释放空间。此时:
|
|
26
|
+
|
|
27
|
+
- DT 会捕获源表的删除事件,并将其反映到增量计算结果中
|
|
28
|
+
- `ROW_NUMBER() OVER (PARTITION BY key ORDER BY ts DESC) WHERE rn = 1` 的删除处理代价极高:
|
|
29
|
+
- 窗口函数无法增量处理删除——需要回读该 key 下的所有历史数据重新排序
|
|
30
|
+
- 非分区表没有分区边界来限制回读范围,可能需要扫描整张表
|
|
31
|
+
- 大规模归档会产生海量删除变更,每个 key 都需要独立回算
|
|
32
|
+
- 一次源表归档可能导致 DT REFRESH 耗时从秒级飙升到小时级甚至失败
|
|
33
|
+
|
|
34
|
+
### 风险 3:无法过滤归档产生的删除事件
|
|
35
|
+
|
|
36
|
+
DT 的增量引擎自动捕获源表的所有变更(INSERT / UPDATE / DELETE),用户无法干预这个过程。SQL 中的 `WHERE op <> 'DELETE'` 过滤的是业务层面的删除标记,而不是源表物理删除产生的删除变更。用户没有任何手段告诉 DT "这些删除是归档操作,请忽略"。
|
|
37
|
+
|
|
38
|
+
## 推荐替代方案
|
|
39
|
+
|
|
40
|
+
建议用户使用 MERGE INTO + Table Stream 替代:
|
|
41
|
+
|
|
42
|
+
```sql
|
|
43
|
+
-- Step 1: 源表开启变更跟踪
|
|
44
|
+
ALTER TABLE source_table SET PROPERTIES ('change_tracking' = 'true');
|
|
45
|
+
|
|
46
|
+
-- Step 2: 创建 Table Stream
|
|
47
|
+
CREATE TABLE STREAM source_stream ON TABLE source_table
|
|
48
|
+
WITH (TABLE_STREAM_MODE = 'STANDARD', SHOW_INITIAL_ROWS = TRUE);
|
|
49
|
+
|
|
50
|
+
-- Step 3: 创建目标表
|
|
51
|
+
CREATE TABLE target_table (
|
|
52
|
+
id BIGINT,
|
|
53
|
+
col1 STRING,
|
|
54
|
+
col2 INT,
|
|
55
|
+
event_time TIMESTAMP
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
-- Step 4: 定时调度 MERGE INTO 消费 Stream
|
|
59
|
+
MERGE INTO target_table t
|
|
60
|
+
USING (
|
|
61
|
+
SELECT id, col1, col2, event_time,
|
|
62
|
+
CASE WHEN `value` IS NULL OR `value` = '' THEN 'DELETE' ELSE 'UPSERT' END AS op
|
|
63
|
+
FROM source_stream
|
|
64
|
+
) s ON t.id = s.id
|
|
65
|
+
WHEN MATCHED AND s.op = 'UPSERT' THEN UPDATE SET
|
|
66
|
+
t.col1 = s.col1, t.col2 = s.col2, t.event_time = s.event_time
|
|
67
|
+
WHEN NOT MATCHED AND s.op = 'UPSERT' THEN INSERT
|
|
68
|
+
(id, col1, col2, event_time) VALUES (s.id, s.col1, s.col2, s.event_time);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
MERGE INTO + Table Stream 的优势:
|
|
72
|
+
- **每次计算独立**:只消费 Stream 中的增量数据,不依赖源表全量状态
|
|
73
|
+
- **归档免疫**:源表归档时,可在 USING 子查询中通过 WHERE 条件过滤归档产生的删除事件
|
|
74
|
+
- **目标表独立管理**:目标表的生命周期与源表解耦,可独立制定归档策略
|
|
75
|
+
- **offset 自动推进**:MERGE INTO 消费 Stream 后 offset 自动推进,下次只处理新变更
|
|
76
|
+
|
|
77
|
+
## 告警话术模板
|
|
78
|
+
|
|
79
|
+
当检测到用户的 DT 满足触发条件时,使用以下话术:
|
|
80
|
+
|
|
81
|
+
> ⚠️ **风险提示**:您正在创建一个非分区的 Dynamic Table,且源表也是非分区的持续写入表。这种组合存在以下长期运维风险:
|
|
82
|
+
>
|
|
83
|
+
> 1. **存储无限膨胀**:源表、DT 目标表、DT 状态表三者都会持续增长,且无法通过 `data_lifecycle` 自动清理
|
|
84
|
+
> 2. **源表归档会引发性能灾难**:当您需要对源表进行归档(迁移历史数据后删除)时,DT 会捕获这些删除事件。由于 SQL 中包含 `ROW_NUMBER() ... WHERE rn = 1` 的去重逻辑,每个被删除的 key 都需要回读历史数据重新排序,非分区表没有边界限制,可能导致 REFRESH 性能严重回退
|
|
85
|
+
> 3. **无法过滤归档删除**:DT 增量引擎自动捕获源表所有变更,您无法告诉 DT 忽略归档操作产生的删除
|
|
86
|
+
>
|
|
87
|
+
> **建议**:对于这类"非分区 CDC 明细表合并为结果表"的场景,推荐使用 MERGE INTO + Table Stream 方案。每次只消费增量数据,源表归档时可通过 WHERE 条件过滤删除事件,不会影响下游。
|
|
88
|
+
|
|
89
|
+
## 判断逻辑
|
|
90
|
+
|
|
91
|
+
在帮助用户创建 DT 时,按以下顺序检查:
|
|
92
|
+
|
|
93
|
+
1. DT 是否有 `PARTITIONED BY` 或 `SESSION_CONFIGS()` → 如果有,不触发告警
|
|
94
|
+
2. 源表是否是持续写入的非分区表(如 Kafka 消费表、CDC 明细表)→ 如果不是,不触发告警
|
|
95
|
+
3. SQL 是否包含 `ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ... DESC) WHERE rn = 1` 模式 → 如果包含,风险最高,必须告警
|
|
96
|
+
4. 即使没有 ROW_NUMBER,只要满足条件 1+2,也应提醒用户注意存储膨胀风险
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Dynamic Table 性能优化指南
|
|
2
|
+
|
|
3
|
+
本文档从 SQL 写法、数据特征、管道设计三个维度,帮助用户写出增量刷新性能更好的 DT。
|
|
4
|
+
|
|
5
|
+
## 核心原则:增量刷新的代价模型
|
|
6
|
+
|
|
7
|
+
增量刷新的性能取决于三个因素:
|
|
8
|
+
1. **变更量占比**:每次刷新时,源表中有多少数据发生了变化。变更量越小,增量越划算
|
|
9
|
+
2. **算子类型**:不同 SQL 算子的增量代价差异很大
|
|
10
|
+
3. **数据局部性**:变更数据在 JOIN key / GROUP BY key / PARTITION BY key 上的分布是否集中
|
|
11
|
+
|
|
12
|
+
当变更量超过总数据量的较大比例时,增量刷新可能反而比全量刷新更慢,因为增量需要额外的变更数据计算、去重合并、状态表读写等开销。
|
|
13
|
+
|
|
14
|
+
## SQL 写法优化
|
|
15
|
+
|
|
16
|
+
### 1. 优先使用 INNER JOIN 而非 OUTER JOIN
|
|
17
|
+
|
|
18
|
+
INNER JOIN 的增量计算比 OUTER JOIN 更高效:
|
|
19
|
+
- INNER JOIN:只需要计算 A 的变更数据 JOIN B 的全量数据 + A 的全量数据 JOIN B 的变更数据
|
|
20
|
+
- LEFT/RIGHT/FULL OUTER JOIN:还需要额外处理 NULL 填充、反向撤回等逻辑
|
|
21
|
+
|
|
22
|
+
如果业务上可以保证参照完整性(即 JOIN key 一定能匹配),优先用 INNER JOIN。
|
|
23
|
+
|
|
24
|
+
```sql
|
|
25
|
+
-- ❌ 不必要的 LEFT JOIN(如果 product 一定存在)
|
|
26
|
+
SELECT o.*, p.name FROM orders o LEFT JOIN products p ON o.pid = p.id;
|
|
27
|
+
|
|
28
|
+
-- ✅ 改用 INNER JOIN
|
|
29
|
+
SELECT o.*, p.name FROM orders o INNER JOIN products p ON o.pid = p.id;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. 减少不必要的 DISTINCT
|
|
33
|
+
|
|
34
|
+
每次增量刷新时,DISTINCT 需要对受影响的 key 做重算。如果上游数据已经去重,或者可以通过其他方式保证唯一性,去掉 DISTINCT。
|
|
35
|
+
|
|
36
|
+
```sql
|
|
37
|
+
-- ❌ 冗余的 DISTINCT
|
|
38
|
+
SELECT DISTINCT user_id, user_name FROM user_events;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. 窗口函数必须有 PARTITION BY
|
|
42
|
+
|
|
43
|
+
没有 PARTITION BY 的窗口函数会导致每次增量刷新都全量重算整个窗口。加上 PARTITION BY 后,只需要重算受影响的分区。
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- ❌ 全局窗口,每次增量都全量重算
|
|
47
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn FROM events;
|
|
48
|
+
|
|
49
|
+
-- ✅ 加上 PARTITION BY,只重算有变更的分区
|
|
50
|
+
SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC) AS rn FROM events;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. 聚合 key 尽量使用简单列引用
|
|
54
|
+
|
|
55
|
+
复合表达式作为 GROUP BY key 会降低增量效率,因为引擎需要对表达式求值后才能判断哪些 key 受影响。
|
|
56
|
+
|
|
57
|
+
```sql
|
|
58
|
+
-- ❌ 复合表达式作为 GROUP BY key
|
|
59
|
+
SELECT DATE_TRUNC('hour', ts) AS hour, SUM(amount)
|
|
60
|
+
FROM transactions
|
|
61
|
+
GROUP BY DATE_TRUNC('hour', ts);
|
|
62
|
+
|
|
63
|
+
-- ✅ 如果可能,在上游预计算好 key 列
|
|
64
|
+
-- 或者拆分为两个 DT(见下文"管道拆分")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 5. 尽可能使用分区条件限制数据范围
|
|
68
|
+
|
|
69
|
+
在 DT 的 SQL 中对源表添加分区过滤条件,可以显著减少每次增量刷新需要扫描的数据量。
|
|
70
|
+
|
|
71
|
+
```sql
|
|
72
|
+
-- ❌ 不加分区条件,每次扫描全表
|
|
73
|
+
SELECT o.*, p.name
|
|
74
|
+
FROM orders o JOIN products p ON o.pid = p.id;
|
|
75
|
+
|
|
76
|
+
-- ✅ 通过分区条件限制数据范围
|
|
77
|
+
SELECT o.*, p.name
|
|
78
|
+
FROM orders o JOIN products p ON o.pid = p.id
|
|
79
|
+
WHERE o.ds = SESSION_CONFIGS()['dt.args.ds'];
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 管道拆分:复杂 DT 拆成多级
|
|
83
|
+
|
|
84
|
+
当一个 DT 的 SQL 包含多个 JOIN + 聚合 + 窗口函数时,考虑拆分为多个 DT,每个 DT 只做一件事。
|
|
85
|
+
|
|
86
|
+
好处:
|
|
87
|
+
- 每个 DT 的增量计算更简单、更快
|
|
88
|
+
- 中间 DT 可以被多个下游 DT 复用
|
|
89
|
+
- 出问题时更容易定位是哪一层的问题
|
|
90
|
+
- 不同层可以使用不同的优化策略
|
|
91
|
+
|
|
92
|
+
## 数据特征与增量效率
|
|
93
|
+
|
|
94
|
+
### 变更量占比
|
|
95
|
+
|
|
96
|
+
增量刷新在变更量占总数据量比例较小时效果最好。经验值:
|
|
97
|
+
- < 5%:增量刷新通常显著优于全量
|
|
98
|
+
- 5% ~ 20%:取决于具体算子和数据分布
|
|
99
|
+
- \> 20%:可能需要评估是否全量刷新更合适
|
|
100
|
+
|
|
101
|
+
### Append-Only 源表
|
|
102
|
+
|
|
103
|
+
如果源表只有 INSERT 没有 UPDATE/DELETE 可以显著优化:
|
|
104
|
+
- 增量引擎知道变更数据只有新增(无撤回),可以跳过去重合并等操作
|
|
105
|
+
- 聚合可以直接累加,不需要维护完整的中间状态
|
|
106
|
+
|
|
107
|
+
### 变更数据的分布
|
|
108
|
+
|
|
109
|
+
如果变更数据集中在少数 key 上(如最近时间段的数据),增量效率高。如果变更分散在大量 key 上,聚合和窗口函数需要重算大量分区,效率下降。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dt-creator
|
|
3
|
+
description: |
|
|
4
|
+
创建 Dynamic Table 的参考资料索引。涵盖静态分区 DT 与动态分区 DT 的声明策略、
|
|
5
|
+
增量计算支持的 SQL 模式、增量刷新配置项说明、以及刷新历史的查询方式。
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# DT Creator — 参考资料索引
|
|
9
|
+
|
|
10
|
+
## references/
|
|
11
|
+
|
|
12
|
+
- **dt-declaration-strategy.md** — DT 声明策略(静态分区 DT vs 动态分区 DT 的创建语法与选择)
|
|
13
|
+
- **sql-limitations.md** — SQL 支持矩阵(JOIN、聚合、窗口函数、非确定性函数等的支持情况)
|
|
14
|
+
- **incremental-config-reference.md** — 增量计算配置参考(刷新策略、源表特征声明、状态表管理等)
|
|
15
|
+
- **refresh-history-guide.md** — 增量刷新历史查询(SHOW REFRESH HISTORY / DESC HISTORY / information_schema)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Dynamic Table 声明策略
|
|
2
|
+
|
|
3
|
+
DT 有两种创建语法:静态分区 DT 和动态分区 DT(非分区 DT 可视为动态分区的特例)。两者在创建语法、刷新方式、增量行为上有本质区别。
|
|
4
|
+
|
|
5
|
+
## 核心概念
|
|
6
|
+
|
|
7
|
+
### 静态分区 DT(Partitioned DT with SESSION_CONFIGS args)
|
|
8
|
+
|
|
9
|
+
SQL 中通过 `SESSION_CONFIGS()` 引用分区参数,每次 REFRESH 时指定具体的分区值。每个分区独立刷新,可以视为每个分区刷新单元都是一个彼此独立的 DT。
|
|
10
|
+
|
|
11
|
+
```sql
|
|
12
|
+
CREATE DYNAMIC TABLE order_daily (
|
|
13
|
+
id BIGINT, amount DECIMAL(12,2), ds STRING
|
|
14
|
+
)
|
|
15
|
+
PARTITIONED BY (ds)
|
|
16
|
+
AS
|
|
17
|
+
SELECT id, amount, SESSION_CONFIGS()['dt.args.ds'] AS ds
|
|
18
|
+
FROM orders
|
|
19
|
+
WHERE ds = SESSION_CONFIGS()['dt.args.ds'];
|
|
20
|
+
|
|
21
|
+
-- 刷新时指定分区(在 Studio 任务中执行,dt.args.ds 通过任务参数传入)
|
|
22
|
+
REFRESH DYNAMIC TABLE order_daily PARTITION(ds = '2025-01-01');
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 动态分区 DT(Non-partitioned DT / DT without args)
|
|
26
|
+
|
|
27
|
+
SQL 中不引用 `SESSION_CONFIGS()`,或者虽然有分区但分区值由查询逻辑动态产生。每次 REFRESH 处理所有源表的增量数据。
|
|
28
|
+
|
|
29
|
+
动态分区 DT 不允许除 REFRESH 以外的任何命令修改数据(INSERT/UPDATE/DELETE/MERGE 均不可用),数据完全由 REFRESH 驱动。
|
|
30
|
+
|
|
31
|
+
因此以下 ETL 场景不适合使用动态分区 DT:
|
|
32
|
+
- 需要手动修补数据(如发现某几行数据有误,需要直接 UPDATE 修正)
|
|
33
|
+
- 需要按条件删除部分数据(如清理脏数据、删除过期记录)
|
|
34
|
+
- 需要 MERGE INTO 做 upsert(如 CDC 场景中消费 stream 合并到目标表)
|
|
35
|
+
- 需要 INSERT INTO 追加外部数据(如手动导入一批补录数据)
|
|
36
|
+
- 需要按分区独立回填或重刷(动态分区 DT 只能整表全量刷新,无法单独刷某个分区)
|
|
37
|
+
- 下游有其他任务需要往同一张表写入数据(DT 独占写入权)
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
CREATE DYNAMIC TABLE order_summary (
|
|
41
|
+
category STRING, total_amount DECIMAL(12,2)
|
|
42
|
+
)
|
|
43
|
+
AS
|
|
44
|
+
SELECT category, SUM(amount) AS total_amount
|
|
45
|
+
FROM orders
|
|
46
|
+
GROUP BY category;
|
|
47
|
+
|
|
48
|
+
-- 刷新时不指定分区
|
|
49
|
+
REFRESH DYNAMIC TABLE order_summary;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 两者的关键区别
|
|
53
|
+
|
|
54
|
+
| 维度 | 静态分区 DT | 动态分区 DT |
|
|
55
|
+
|------|-----------|-----------|
|
|
56
|
+
| SQL 中是否有 `SESSION_CONFIGS()` | 有,用于引用分区参数 | 无 |
|
|
57
|
+
| REFRESH 语法 | `REFRESH ... PARTITION(ds='xxx')` | `REFRESH ...`(无 PARTITION) |
|
|
58
|
+
| 增量范围 | 只处理指定分区的增量数据 | 处理所有源表的全部增量数据 |
|
|
59
|
+
| 调度方式 | 外部调度器按分区值逐个触发 | 外部调度器定时触发即可 |
|
|
60
|
+
| 数据生命周期 | 按分区管理,可独立回填/删除 | 整表管理 |
|
|
61
|
+
| 状态表 | 按分区独立维护 | 全局维护 |
|
|
62
|
+
| 适合的数据模式 | T+1 批处理、按时间分区的 ETL | 实时流、全局聚合、无明确分区键 |
|
|
63
|
+
|
|
64
|
+
## 选择决策树
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
你的数据有明确的时间/业务分区键吗?
|
|
68
|
+
│
|
|
69
|
+
├─ 是 → 原始 ETL 是按分区 INSERT OVERWRITE 的吗?
|
|
70
|
+
│ │
|
|
71
|
+
│ ├─ 是 → 使用静态分区 DT
|
|
72
|
+
│ │ (保持原有的分区粒度,每个分区独立刷新)
|
|
73
|
+
│ │
|
|
74
|
+
│ └─ 否 → 数据量大吗?需要按分区管理生命周期吗?
|
|
75
|
+
│ │
|
|
76
|
+
│ ├─ 是 → 使用静态分区 DT
|
|
77
|
+
│ │ (即使原来不是分区表,也建议加分区以便管理)
|
|
78
|
+
│ │
|
|
79
|
+
│ └─ 否 → 使用动态分区 DT
|
|
80
|
+
│ (简单场景,不需要分区管理)
|
|
81
|
+
│
|
|
82
|
+
└─ 否 → 使用动态分区 DT
|
|
83
|
+
(全局聚合、实时汇总等场景)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 静态分区 DT 详解
|
|
87
|
+
|
|
88
|
+
### 适用场景
|
|
89
|
+
|
|
90
|
+
1. **T+1 批处理 ETL 迁移**
|
|
91
|
+
- 原始 SQL 是 `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}')` 模式
|
|
92
|
+
- 每天/每小时按分区刷新一次
|
|
93
|
+
- 需要支持历史分区回填
|
|
94
|
+
|
|
95
|
+
2. **滑动窗口计算**
|
|
96
|
+
- 如:最近 7 天的聚合、环比计算
|
|
97
|
+
- SQL 中引用 `SESSION_CONFIGS()['dt.args.ds']` 和 `sub_days(...)` 做窗口范围
|
|
98
|
+
|
|
99
|
+
3. **需要按分区管理数据生命周期**
|
|
100
|
+
- 通过 `data_lifecycle` 自动清理过期分区
|
|
101
|
+
- 可以单独回填某个分区而不影响其他分区
|
|
102
|
+
|
|
103
|
+
4. **自引用 DT(日环比、SCD)**
|
|
104
|
+
- 当前分区依赖上一个分区的结果
|
|
105
|
+
- 必须用静态分区,因为需要明确指定"当前分区"和"上一分区"
|
|
106
|
+
|
|
107
|
+
### 刷新方式
|
|
108
|
+
|
|
109
|
+
> ⚠️ **重要**:静态分区 DT 的 `dt.args.*` 参数**仅在 Studio 任务中可用**,不能在交互式 SQL 中使用 `SET dt.args.xxx`。
|
|
110
|
+
> 必须通过 Studio 创建调度任务,在任务参数中配置 `dt.args.ds` 等值。
|
|
111
|
+
|
|
112
|
+
```sql
|
|
113
|
+
-- 在 Studio 任务中,参数通过任务配置传入,REFRESH 语句指定分区值:
|
|
114
|
+
REFRESH DYNAMIC TABLE my_dt PARTITION(ds = '2025-01-15');
|
|
115
|
+
|
|
116
|
+
-- 多级分区
|
|
117
|
+
REFRESH DYNAMIC TABLE my_dt PARTITION(pt = '20250411', pt_hour = '01');
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 注意事项
|
|
121
|
+
|
|
122
|
+
- 回填时使用 `SET cz.optimizer.incremental.backfill.enabled = TRUE`(此配置在交互式 SQL 中可用)
|
|
123
|
+
- `dt.args.*` 参数仅在 Studio 任务中可用,不能在交互式 SQL 中 SET
|
|
124
|
+
- `cz.optimizer.*` 配置项在交互式 SQL 中可用
|
|
125
|
+
|
|
126
|
+
## 动态分区 DT 详解
|
|
127
|
+
|
|
128
|
+
### 适用场景
|
|
129
|
+
|
|
130
|
+
1. **实时流数据聚合**
|
|
131
|
+
- 源表持续写入,DT 定时刷新
|
|
132
|
+
- 不需要按分区管理,每次处理所有新增数据
|
|
133
|
+
|
|
134
|
+
2. **全局汇总表**
|
|
135
|
+
- 如:全局 TopN、全局计数、全局去重
|
|
136
|
+
- 没有明确的分区键
|
|
137
|
+
|
|
138
|
+
3. **简单的 JOIN + 过滤**
|
|
139
|
+
- 不涉及分区参数的简单转换
|
|
140
|
+
- 如:事实表 JOIN 维度表,输出宽表
|
|
141
|
+
|
|
142
|
+
4. **多源表合并(UNION ALL)**
|
|
143
|
+
- 多个源表的数据合并到一张表
|
|
144
|
+
- 不需要按分区管理
|
|
145
|
+
|
|
146
|
+
### 刷新方式
|
|
147
|
+
|
|
148
|
+
```sql
|
|
149
|
+
-- 直接刷新,处理所有源表的增量
|
|
150
|
+
REFRESH DYNAMIC TABLE my_dt;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 注意事项
|
|
154
|
+
|
|
155
|
+
- 每次刷新处理所有源表的全部增量,如果源表变更量大,刷新可能较慢
|
|
156
|
+
- 状态表全局维护,随着数据量增长可能膨胀
|
|
157
|
+
- 不支持按分区回填,只能全量刷新整表
|
|
158
|
+
- 适合变更量占比小的场景(< 5%)
|
|
159
|
+
|
|
160
|
+
## 分区粒度选择
|
|
161
|
+
|
|
162
|
+
当选择静态分区 DT 时,还需要决定分区粒度:
|
|
163
|
+
|
|
164
|
+
| 数据模式 | 推荐分区粒度 | 说明 |
|
|
165
|
+
|---------|------------|------|
|
|
166
|
+
| 严格有序的时间序列(如日志) | 分钟级 (`dt_min`) | 数据量大、写入频繁 |
|
|
167
|
+
| 大致有序、少量迟到数据 | 小时级 (`dt_hour`) | 平衡粒度和管理复杂度 |
|
|
168
|
+
| T+1 批量导入 | 天级 (`ds`) | 最常见的 ETL 场景 |
|
|
169
|
+
| 按业务周期 | 周/月级 | 报表类场景 |
|
|
170
|
+
| 多级分区 | 天 + 小时 (`ds`, `hour`) | 需要更细粒度的生命周期管理 |
|
|
171
|
+
|
|
172
|
+
选择原则:
|
|
173
|
+
- 粒度越细,每次刷新处理的数据量越小,增量效率越高
|
|
174
|
+
- 粒度越细,分区数越多,管理和调度越复杂
|
|
175
|
+
- 粒度应与数据写入频率匹配:如果数据每小时写入一次,分区粒度不应细于小时
|
|
176
|
+
|
|
177
|
+
## 从原始 ETL 判断分区策略
|
|
178
|
+
|
|
179
|
+
| 原始 ETL 模式 | 推荐 DT 分区策略 |
|
|
180
|
+
|--------------|----------------|
|
|
181
|
+
| `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}')` | 静态分区 DT,天级 |
|
|
182
|
+
| `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}', hour='${hour}')` | 静态分区 DT,天+小时级 |
|
|
183
|
+
| `INSERT OVERWRITE TABLE t PARTITION(ds)` (动态分区写入) | 动态分区 DT 或静态分区 DT(取决于是否需要按分区管理) |
|
|
184
|
+
| `INSERT INTO TABLE t SELECT ...` (无分区) | 动态分区 DT |
|
|
185
|
+
| `INSERT OVERWRITE TABLE t SELECT ...` (全表覆盖) | 动态分区 DT |
|