@clickzetta/cz-cli-darwin-x64 0.3.16 → 0.3.18

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.
Files changed (41) hide show
  1. package/bin/cz-cli +0 -0
  2. package/bin/skills/clickzetta-batch-sync-pipeline/SKILL.md +386 -0
  3. package/bin/skills/clickzetta-cdc-sync-pipeline/SKILL.md +548 -0
  4. package/bin/skills/clickzetta-data-ingest-pipeline/SKILL.md +220 -0
  5. package/bin/skills/clickzetta-data-ingest-pipeline/eval_cases.jsonl +5 -0
  6. package/bin/skills/clickzetta-dynamic-table/SKILL.md +112 -0
  7. package/bin/skills/clickzetta-dynamic-table/best-practices/dimension-table-join-guide.md +257 -0
  8. package/bin/skills/clickzetta-dynamic-table/best-practices/medallion-and-stream-patterns.md +124 -0
  9. package/bin/skills/clickzetta-dynamic-table/best-practices/non-partitioned-merge-into-warning.md +96 -0
  10. package/bin/skills/clickzetta-dynamic-table/best-practices/performance-optimization.md +109 -0
  11. package/bin/skills/clickzetta-file-import-pipeline/SKILL.md +156 -0
  12. package/bin/skills/clickzetta-kafka-ingest-pipeline/SKILL.md +751 -0
  13. package/bin/skills/clickzetta-kafka-ingest-pipeline/eval_cases.jsonl +5 -0
  14. package/bin/skills/clickzetta-kafka-ingest-pipeline/references/kafka-pipe-syntax.md +324 -0
  15. package/bin/skills/clickzetta-oss-ingest-pipeline/SKILL.md +537 -0
  16. package/bin/skills/clickzetta-query-optimizer/SKILL.md +156 -0
  17. package/bin/skills/clickzetta-query-optimizer/references/explain.md +56 -0
  18. package/bin/skills/clickzetta-query-optimizer/references/hints-and-sortkey.md +78 -0
  19. package/bin/skills/clickzetta-query-optimizer/references/optimize.md +65 -0
  20. package/bin/skills/clickzetta-query-optimizer/references/result-cache.md +49 -0
  21. package/bin/skills/clickzetta-query-optimizer/references/show-jobs.md +42 -0
  22. package/bin/skills/clickzetta-realtime-sync-pipeline/SKILL.md +276 -0
  23. package/bin/skills/clickzetta-sql-pipeline-manager/SKILL.md +379 -0
  24. package/bin/skills/clickzetta-sql-pipeline-manager/evals/evals.json +166 -0
  25. package/bin/skills/clickzetta-sql-pipeline-manager/references/dynamic-table.md +185 -0
  26. package/bin/skills/clickzetta-sql-pipeline-manager/references/materialized-view.md +129 -0
  27. package/bin/skills/clickzetta-sql-pipeline-manager/references/pipe.md +222 -0
  28. package/bin/skills/clickzetta-sql-pipeline-manager/references/table-stream.md +125 -0
  29. package/bin/skills/clickzetta-table-stream-pipeline/SKILL.md +206 -0
  30. package/bin/skills/clickzetta-vcluster-manager/SKILL.md +212 -0
  31. package/bin/skills/clickzetta-vcluster-manager/references/vc-cache.md +54 -0
  32. package/bin/skills/clickzetta-vcluster-manager/references/vcluster-ddl.md +150 -0
  33. package/bin/skills/clickzetta-volume-manager/SKILL.md +292 -0
  34. package/bin/skills/clickzetta-volume-manager/references/volume-ddl.md +199 -0
  35. package/package.json +1 -1
  36. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/SKILL.md +0 -0
  37. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/dt-declaration-strategy.md +0 -0
  38. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/incremental-config-reference.md +0 -0
  39. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/refresh-history-guide.md +0 -0
  40. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/sql-limitations.md +0 -0
  41. /package/bin/skills/{dynamic-table-alter → clickzetta-dynamic-table/dynamic-table-alter}/SKILL.md +0 -0
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: clickzetta-data-ingest-pipeline
3
+ description: |
4
+ ClickZetta Lakehouse 数据导入总览与路由。根据用户的数据源类型、实时性要求、数据量等条件,
5
+ 推荐最合适的数据导入方式,并引导到对应的专项 Skill 或直接执行简单导入操作。
6
+ 当用户说"导入数据到 Lakehouse"、"数据入仓"、"数据入湖"、"怎么把数据导进来"、
7
+ "数据采集"、"数据加载"、"ingest data"、"load data"、"数据导入方案选择"时触发。
8
+ Keywords: data ingestion, import, routing, pipeline selection, data source
9
+ ---
10
+
11
+ # Lakehouse 数据导入总览与路由
12
+
13
+ 根据用户的数据源、实时性需求、数据规模等条件,推荐最合适的数据导入方式,
14
+ 并路由到对应的专项 Pipeline Skill 或直接执行简单导入操作。
15
+
16
+ ## 适用场景
17
+
18
+ - 用户想把数据导入 ClickZetta Lakehouse,但不确定用哪种方式
19
+ - 用户描述了数据源(Kafka、MySQL、OSS、文件等),需要推荐导入方案
20
+ - 用户需要了解各种导入方式的适用场景和差异
21
+ - 关键词:数据导入、数据入仓、数据入湖、数据采集、数据加载、pipeline 选择
22
+
23
+ ## 前置依赖
24
+
25
+ - ClickZetta Lakehouse 账户,具备创建工作空间、Schema、表、PIPE、任务等权限
26
+ - **执行环境(满足其一即可,优先使用 cz-cli)**:
27
+ - **cz-cli 路径**:已安装 cz-cli(`pip install cz-cli`),并完成 `cz-cli configure` 配置
28
+ - **MCP 路径**:clickzetta-studio-mcp 或 clickzetta-mcp-server 工具可用(`LH_execute_query`、`create_task`、`save_integration_task` 等)
29
+
30
+ ## 环境探测(执行前必读)
31
+
32
+ 在开始任何操作前,先判断当前执行环境:
33
+
34
+ **第一步:检测 cz-cli 是否可用**
35
+ ```bash
36
+ cz-cli --version
37
+ ```
38
+ - 若命令存在 → **走 cz-cli 路径**(见本文档末尾"cz-cli 替代路径"章节,以及各专项 Skill 的 cz-cli 替代路径)
39
+ - 若命令不存在 → 继续检测 MCP
40
+
41
+ **第二步:检测 MCP 是否可用(仅在 cz-cli 不可用时)**
42
+
43
+ 尝试调用 `LH_execute_query` 工具执行一条简单 SQL(如 `SELECT 1`)。
44
+ - 若工具存在于 tool list → **走 MCP 路径**(本文档默认路径)
45
+ - 若工具不存在 → 停止执行,提示用户:
46
+ > "当前环境既无 cz-cli 也无 MCP 工具,请安装其中之一后重试。
47
+ > cz-cli 安装:`pip install cz-cli`,然后运行 `cz-cli configure`
48
+ > MCP 安装:参考 clickzetta-studio-mcp 或 clickzetta-mcp-server 配置文档"
49
+
50
+ ## 数据导入方式决策树
51
+
52
+ ### 步骤 1:确认数据源类型和需求
53
+
54
+ 向用户收集以下信息:
55
+
56
+ 1. **数据源类型**:Kafka / 对象存储(OSS/S3/COS) / 关系型数据库(MySQL/PostgreSQL/SQL Server) / 本地文件 / URL/Web 文件 / Java SDK / ZettaPark
57
+ 2. **实时性要求**:实时(秒级延迟)/ 准实时(分钟级)/ 离线批量(小时/天级)
58
+ 3. **同步范围**:单表 / 多表 / 整库
59
+ 4. **是否需要持续同步**:一次性导入 / 持续增量同步
60
+ 5. **是否需要 CDC(变更数据捕获)**:是 / 否
61
+
62
+ ### 步骤 2:根据决策矩阵推荐方案
63
+
64
+ | 数据源 | 实时性 | 同步范围 | 推荐方式 | 对应 Skill |
65
+ |--------|--------|---------|---------|-----------|
66
+ | Kafka | 实时/准实时 | 单 topic | Kafka PIPE 持续导入(SQL) | `clickzetta-kafka-ingest-pipeline` |
67
+ | Kafka | 实时 | 多 topic | Studio 实时同步 | `clickzetta-realtime-sync-pipeline` |
68
+ | 对象存储 (OSS/S3/COS) | 准实时/批量 | 文件持续到达 | PIPE 持续导入 | `clickzetta-oss-ingest-pipeline` |
69
+ | 对象存储 | 一次性 | 批量文件 | COPY INTO 命令 | `clickzetta-file-import-pipeline`(COPY INTO 部分) |
70
+ | MySQL/PostgreSQL/SQL Server | 实时 CDC | 单表 | Studio 实时同步 | `clickzetta-realtime-sync-pipeline` |
71
+ | MySQL/PostgreSQL/SQL Server | 实时 CDC | 多表/整库 | Studio 多表实时同步 | `clickzetta-cdc-sync-pipeline` |
72
+ | MySQL/PostgreSQL/SQL Server | 离线批量 | 单表 | Studio 离线同步 | `clickzetta-batch-sync-pipeline` |
73
+ | MySQL/PostgreSQL/SQL Server | 离线批量 | 多表 | Studio 多表离线同步 | `clickzetta-batch-sync-pipeline` |
74
+ | 本地文件 / URL | 一次性 | 单文件/多文件 | URL 下载 + COPY INTO | `clickzetta-file-import-pipeline` |
75
+ | 流式增量计算 | 准实时 | 表变更驱动 | Dynamic Table + Stream | `clickzetta-incremental-compute-pipeline` |
76
+ | Java 应用 | 实时/批量 | 程序写入 | Java SDK | (见下方 SDK 导入指引) |
77
+ | Python/ZettaPark | 批量 | DataFrame | ZettaPark save_as_table | (见下方 SDK 导入指引) |
78
+
79
+ ### 步骤 3:路由到专项 Skill 或直接执行
80
+
81
+ 根据推荐方案,执行以下路由逻辑:
82
+
83
+ **有对应专项 Skill 的场景** → 告知用户推荐方案,引导使用对应 Skill:
84
+ - `clickzetta-kafka-ingest-pipeline`:Kafka PIPE 管道搭建
85
+ - `clickzetta-oss-ingest-pipeline`:对象存储 PIPE 管道搭建
86
+ - `clickzetta-batch-sync-pipeline`:Studio 离线同步任务
87
+ - `clickzetta-realtime-sync-pipeline`:Studio 实时同步任务
88
+ - `clickzetta-cdc-sync-pipeline`:Studio 多表实时同步(CDC)
89
+ - `clickzetta-incremental-compute-pipeline`:Dynamic Table + Stream 增量计算管道
90
+ - `clickzetta-file-import-pipeline`:URL/文件下载导入
91
+ - `clickzetta-table-stream-pipeline`:Table Stream 变更数据捕获
92
+
93
+ **无专项 Skill 的简单场景** → 直接执行:
94
+
95
+ #### SQL INSERT 导入(小数据量)
96
+ ```sql
97
+ -- 使用 LH_execute_query 执行
98
+ INSERT INTO schema_name.table_name (col1, col2, col3)
99
+ VALUES ('val1', 'val2', 'val3');
100
+ ```
101
+
102
+ #### COPY INTO 快速导入(从 Volume)
103
+ ```sql
104
+ -- 1. 确认 Volume 中有文件
105
+ SHOW VOLUME DIRECTORY volume_name;
106
+
107
+ -- 2. 执行 COPY INTO
108
+ COPY INTO schema_name.table_name
109
+ FROM VOLUME volume_name
110
+ USING CSV
111
+ OPTIONS('header' = 'true');
112
+ ```
113
+
114
+ #### Java SDK 导入指引
115
+ 提供 Java SDK 的关键配置信息:
116
+ - Maven 依赖坐标
117
+ - 连接配置(endpoint、workspace、schema、vcluster)
118
+ - 批量写入 API:`BulkloadWriter`
119
+ - 实时写入 API:`RealtimeWriter`
120
+ - 建议用户参考官方文档:`comprehensive_guide_to_ingesting_javasdk_buckload_realtime`
121
+
122
+ #### ZettaPark (Python) 导入指引
123
+ - `INSERT` 方式:`session.sql("INSERT INTO ...")`
124
+ - `save_as_table` 方式:`df.write.save_as_table("table_name")`
125
+ - 建议用户参考官方文档:`comprehensive_guide_to_ingesting_zettapark_save_as_table`
126
+
127
+ ## 数据入仓 vs 数据入湖
128
+
129
+ | 维度 | 数据入仓 | 数据入湖 |
130
+ |------|---------|---------|
131
+ | 目标 | Lakehouse 托管表 | 用户 Volume(对象存储) |
132
+ | 格式 | 自动转为内部列式格式 | 保持原始文件格式 |
133
+ | 查询性能 | 高(列式存储 + 索引) | 较低(需扫描原始文件) |
134
+ | 适用场景 | 分析查询、BI 报表、数据仓库 | 数据暂存、原始数据归档、跨系统共享 |
135
+ | 常用方式 | Studio 同步、PIPE、COPY INTO、SDK | PUT 文件、Python 脚本上传 |
136
+
137
+ ## 示例
138
+
139
+ ### 示例 1:用户不确定导入方式
140
+
141
+ 用户说:"我有一个 MySQL 数据库,想把里面的订单表实时同步到 Lakehouse"
142
+
143
+ 路由逻辑:
144
+ 1. 数据源:MySQL(关系型数据库)
145
+ 2. 实时性:实时
146
+ 3. 同步范围:单表
147
+ 4. 需要 CDC:是(实时同步意味着需要捕获变更)
148
+ → 推荐:Studio 实时同步
149
+ → 路由到 `clickzetta-realtime-sync-pipeline` Skill
150
+
151
+ ### 示例 2:多种数据源混合场景
152
+
153
+ 用户说:"我们有 Kafka 的用户行为日志,还有 MySQL 的业务数据,都要导入 Lakehouse"
154
+
155
+ 路由逻辑:
156
+ 1. Kafka 用户行为日志 → `clickzetta-kafka-ingest-pipeline`(PIPE 持续导入)
157
+ 2. MySQL 业务数据 → 确认实时性需求:
158
+ - 实时 → `clickzetta-realtime-sync-pipeline` 或 `clickzetta-cdc-sync-pipeline`
159
+ - 离线 → `clickzetta-batch-sync-pipeline`
160
+ → 分别引导到对应 Skill
161
+
162
+ ### 示例 3:简单的一次性文件导入
163
+
164
+ 用户说:"我有一个 CSV 文件要导入"
165
+
166
+ 路由逻辑:
167
+ 1. 数据源:本地文件
168
+ 2. 一次性导入
169
+ → 路由到 `clickzetta-file-import-pipeline` Skill(支持文件上传 + COPY INTO)
170
+
171
+ ## 错误处理
172
+
173
+ | 场景 | 处理方式 |
174
+ |------|---------|
175
+ | 用户无法确定数据源类型 | 询问数据当前存储位置(哪个系统/服务),帮助判断 |
176
+ | 用户需求跨多种导入方式 | 拆分为多个独立的导入任务,分别路由到对应 Skill |
177
+ | 推荐的 Skill 尚未创建 | 提供该导入方式的基本步骤和关键 SQL/API,引导用户参考官方文档 |
178
+ | 用户的云环境不支持某种连接 | 使用 `LH_show_object_list`(object_type=CONNECTIONS)检查可用连接类型,推荐替代方案 |
179
+ | 数据量极大(TB 级) | 建议分批导入,优先使用 PIPE 或 Studio 同步任务(支持断点续传) |
180
+
181
+ ## 注意事项
182
+
183
+ - 本 Skill 是路由入口,不直接执行复杂的 pipeline 搭建,而是引导到专项 Skill
184
+ - 对于简单场景(SQL INSERT、单次 COPY INTO),可以直接在本 Skill 中完成
185
+ - 推荐方案时需考虑用户的云环境(阿里云/腾讯云/AWS),不同环境支持的连接类型可能不同
186
+ - 使用 `LH_show_object_list`(object_type=VCLUSTERS)确认可用的虚拟集群,同步任务需要 SYNC 类型的 VCluster
187
+ - 数据入仓是最常见的场景,数据入湖主要用于原始数据暂存或跨系统共享
188
+
189
+ ---
190
+
191
+ ## cz-cli 替代路径
192
+
193
+ > 仅在 cz-cli 可用且 MCP 不可用时使用本节。
194
+ > 本 Skill 是路由入口,cz-cli 路径的核心逻辑在各专项 Skill 的"cz-cli 替代路径"章节中。
195
+
196
+ ### 路由说明
197
+
198
+ 当 MCP 不可用时,各专项 Skill 均已提供 cz-cli 替代路径:
199
+
200
+ | 数据源 | 推荐方式 | 对应 Skill 的 cz-cli 路径 |
201
+ |--------|---------|--------------------------|
202
+ | Kafka | PIPE 持续导入 | `clickzetta-kafka-ingest-pipeline` → cz-cli 替代路径 |
203
+ | 对象存储 (OSS/S3/COS) | PIPE 持续导入 | `clickzetta-oss-ingest-pipeline` → cz-cli 替代路径 |
204
+ | MySQL/PostgreSQL/SQL Server(实时单表) | Studio 实时同步 | `clickzetta-realtime-sync-pipeline` → cz-cli 替代路径 |
205
+ | MySQL/PostgreSQL/SQL Server(实时多表/整库) | Studio 多表实时同步 | `clickzetta-cdc-sync-pipeline` → cz-cli 替代路径 |
206
+ | MySQL/PostgreSQL/SQL Server(离线批量) | Studio 离线同步 | `clickzetta-batch-sync-pipeline` → cz-cli 替代路径 |
207
+
208
+ ### 简单场景直接执行(cz-cli 版)
209
+
210
+ 对于无需专项 Skill 的简单场景,可直接用 cz-cli agent 完成:
211
+
212
+ ```bash
213
+ # SQL INSERT 导入(小数据量)
214
+ cz-cli agent run "向表 <schema_name>.<table_name> 插入数据:<col1>=<val1>, <col2>=<val2>" \
215
+ --format a2a --dangerously-skip-permissions
216
+
217
+ # COPY INTO 快速导入(从 Volume)
218
+ cz-cli agent run "从 Volume <volume_name> 以 CSV 格式(有 header)将数据导入表 <schema_name>.<table_name>" \
219
+ --format a2a --dangerously-skip-permissions
220
+ ```
@@ -0,0 +1,5 @@
1
+ {"case_id":"001","type":"should_call","user_input":"我想把数据导入 Lakehouse,但不确定用哪种方式","expected_skill":"clickzetta-data-ingest-pipeline","expected_output_contains":["数据源","实时","批量"]}
2
+ {"case_id":"002","type":"should_call","user_input":"数据入仓有哪些方案?怎么选择?","expected_skill":"clickzetta-data-ingest-pipeline","expected_output_contains":["Kafka","对象存储","MySQL"]}
3
+ {"case_id":"003","type":"should_call","user_input":"我有 MySQL 和 Kafka 两个数据源要导入 Lakehouse,分别用什么方式?","expected_skill":"clickzetta-data-ingest-pipeline","expected_output_contains":["CDC"]}
4
+ {"case_id":"004","type":"should_call","user_input":"数据导入方案怎么选?实时和离线有什么区别?","expected_skill":"clickzetta-data-ingest-pipeline","expected_output_contains":["实时","离线","延迟"]}
5
+ {"case_id":"005","type":"should_call","user_input":"ingest data into ClickZetta Lakehouse, what options do I have?","expected_skill":"clickzetta-data-ingest-pipeline","expected_output_contains":["Kafka","OSS","SDK"]}
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: clickzetta-dynamic-table
3
+ description: |
4
+ ClickZetta Dynamic Table(动态表)使用指南,覆盖动态表的创建、修改、增量计算配置和性能优化。
5
+ 包含 DT 声明策略(静态分区 DT vs 动态分区 DT)、SQL 支持矩阵、增量配置参考、刷新历史查询、
6
+ ALTER 操作指南,以及维度表 JOIN、性能优化、非分区表风险等最佳实践。
7
+ 当用户说"Dynamic Table"、"动态表"、"自动刷新"、"增量刷新"、"物化视图"、
8
+ "REFRESH interval"、"CREATE DYNAMIC TABLE"、
9
+ "数据管道自动化"、"增量计算"、"自动物化"、"定时刷新"、
10
+ "依赖刷新"、"SESSION_CONFIGS"、"静态分区DT"、"动态分区DT"、
11
+ "状态表"、"state table"、"MERGE INTO"、"Table Stream"时触发。
12
+ Keywords: dynamic table, incremental refresh, REFRESH interval, materialized, auto-refresh pipeline, SESSION_CONFIGS, partitioned DT, state table, MERGE INTO
13
+ ---
14
+
15
+ # Dynamic Table 使用指南 — 目录索引
16
+
17
+ ## 快速入门
18
+
19
+ ```sql
20
+ -- 1. 创建 Dynamic Table(自动调度刷新)
21
+ CREATE DYNAMIC TABLE IF NOT EXISTS silver.orders_daily
22
+ REFRESH INTERVAL 60 MINUTE vcluster default
23
+ AS
24
+ SELECT DATE(created_at) AS order_date, region, SUM(amount) AS total_amount
25
+ FROM bronze.raw_orders
26
+ GROUP BY 1, 2;
27
+
28
+ -- 2. 查看状态与刷新历史
29
+ DESC DYNAMIC TABLE silver.orders_daily;
30
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'orders_daily' LIMIT 10;
31
+
32
+ -- 3. 手动触发刷新
33
+ REFRESH DYNAMIC TABLE silver.orders_daily;
34
+
35
+ -- 4. 列出所有 Dynamic Table
36
+ SHOW TABLES IN silver WHERE is_dynamic;
37
+ -- 返回列:schema_name, table_name, is_view, is_materialized_view, is_external, is_dynamic
38
+ -- ⚠️ 列名是 table_name(不是 name),过滤用 WHERE table_name = 'xxx'
39
+
40
+ -- 5. 查看指定表是否为动态表
41
+ SHOW TABLES IN silver WHERE table_name = 'orders_daily';
42
+ ```
43
+
44
+ ### 调度方式
45
+
46
+ | 方式 | 语法 | 适用场景 |
47
+ |---|---|---|
48
+ | 自动调度 | `REFRESH INTERVAL 10 MINUTE vcluster <name>` | 系统按间隔自动刷新(推荐) |
49
+ | 指定开始时间 | `REFRESH START WITH TIMESTAMP '2025-01-01 00:00:00' INTERVAL 1 HOUR vcluster <name>` | 从指定时间开始调度 |
50
+ | 手动触发 | `REFRESH DYNAMIC TABLE my_dt;` | 外部调度器触发,适合静态分区 DT |
51
+
52
+ INTERVAL 支持的单位:`SECOND`、`MINUTE`、`HOUR`、`DAY`,最小值为 1 分钟。
53
+
54
+ > 建议使用 GP 型集群刷新动态表。动态表刷新过程中会自动执行小文件合并,AP 型集群不支持此功能。
55
+ > ⚠️ **VCluster 类型限制**:创建动态表时如果指定了 AP 型集群(如 `default_ap`),刷新仍可执行但不会进行小文件合并,长期运行可能导致查询性能下降。建议始终使用 GP 型集群(如 `default`)。
56
+
57
+ ### 开启增量刷新的前提
58
+
59
+ 源表需开启变更跟踪:
60
+ ```sql
61
+ ALTER TABLE bronze.raw_orders SET PROPERTIES ('change_tracking' = 'true');
62
+ ```
63
+
64
+ ### 增量刷新 vs 全量刷新
65
+
66
+ 通过 `SHOW DYNAMIC TABLE REFRESH HISTORY` 的 `refresh_mode` 字段可查看刷新模式:
67
+ - `INCREMENTAL`:增量刷新(仅处理变更数据,高效)
68
+ - `FULL`:全量刷新(重新计算所有数据)
69
+ - `NO_DATA`:无数据变更,跳过刷新
70
+
71
+ **触发全量刷新的条件**:
72
+ | 条件 | 说明 |
73
+ |---|---|
74
+ | 源表未开启 `change_tracking` | 系统无法识别增量数据 |
75
+ | 查询含不支持增量的算子 | 如某些复杂 JOIN、子查询 |
76
+ | `CREATE OR REPLACE` 修改了计算逻辑 | 如修改 WHERE、GROUP BY、JOIN key |
77
+ | 手动设置强制全量 | `SET cz.optimizer.incremental.force.full.refresh = true` |
78
+ | 维度表变更 | 被 JOIN 的维度表数据变化时,增量结果可能不一致 |
79
+
80
+ **确认是否支持增量刷新**:
81
+ ```sql
82
+ SET cz.optimizer.explain.can.incrementalize = true;
83
+ EXPLAIN REFRESH DYNAMIC TABLE my_dt;
84
+ -- 查看 CanBeIncrementalized 字段:Yes = 支持增量,No = 不支持(会给出原因)
85
+ ```
86
+
87
+ ---
88
+
89
+ ## dt-creator/
90
+ 创建 Dynamic Table 的参考资料(声明策略、SQL 支持矩阵、增量配置、刷新历史查询)。
91
+
92
+ ## dynamic-table-alter/
93
+ 修改 Dynamic Table 的结构和属性(suspend/resume、加列删列、改刷新间隔等)。
94
+
95
+ ## best-practices/
96
+ Dynamic Table 最佳实践与避坑指南(维度表 JOIN 场景、性能优化、非分区表风险告警)。
97
+
98
+ ---
99
+
100
+ ## 常见问题排障
101
+
102
+ | 问题 | 原因 | 解决方案 |
103
+ |---|---|---|
104
+ | 刷新一直是 FULL 模式 | 源表未开启 change_tracking,或查询含不支持增量的算子 | 开启 change_tracking;用 `EXPLAIN REFRESH` 检查 |
105
+ | 刷新延迟超过预期 | VCluster 资源不足,或查询复杂度高 | 升级 VCluster 规格;拆分管道 |
106
+ | `SUSPEND` 后数据不更新 | 已暂停 | 执行 `ALTER DYNAMIC TABLE ... RESUME` |
107
+ | 依赖链中下游不刷新 | 上游 Dynamic Table 刷新失败 | 先修复上游,再手动 `REFRESH` 下游 |
108
+ | 删除报错 | 有下游 Dynamic Table 依赖 | 先删除下游,再删除上游 |
109
+ | 增量结果与全量不一致 | 维度表变更未触发重算 | 执行全量刷新:`SET cz.optimizer.incremental.force.full.refresh = true` |
110
+ | 状态表损坏 | 系统异常 | `SET cz.optimizer.incremental.rebuild.rule.based.state.table = true` |
111
+ | 手动 REFRESH 后历史未显示 | 刷新历史有短暂延迟 | 等待几秒后重新查询 `SHOW DYNAMIC TABLE REFRESH HISTORY` |
112
+ | AP 集群刷新后查询变慢 | AP 集群不支持小文件合并 | 改用 GP 型集群(`CREATE OR REPLACE` 重建) |
@@ -0,0 +1,257 @@
1
+ # 维度表 JOIN 场景详解
2
+
3
+ ## 核心机制
4
+
5
+ 将某张表标记为维度表(dimension table)后,增量引擎会将该表的变更数据视为**空**。即:
6
+ - 维度表的任何数据变更(INSERT/UPDATE/DELETE)都**不会触发增量计算**
7
+ - 增量计算时,维度表始终读取**最新全量数据**
8
+ - 只有非维度表(事实表)的变更才会驱动增量刷新
9
+
10
+ ## 配置方式
11
+
12
+ ```sql
13
+ -- 方式1:DT 表属性(推荐,跟随 DT 定义)
14
+ CREATE DYNAMIC TABLE my_dt
15
+ TBLPROPERTIES('mv_const_tables'='dim_table1,dim_table2')
16
+ AS SELECT ...;
17
+
18
+ -- 方式2:Session 配置(在 REFRESH 前设置,灵活可动态调整)
19
+ -- set CZ_OPTIMIZER_INCREMENTAL_DIMENSION_TABLES=dim_table1:dim_table2
20
+
21
+ -- 查看已设置的 TBLPROPERTIES(使用 SHOW CREATE TABLE,不支持 SHOW TBLPROPERTIES)
22
+ SHOW CREATE TABLE my_dt;
23
+ ```
24
+
25
+ ## 各 JOIN 类型下的增量行为
26
+
27
+ ### A LEFT JOIN B(B 为维度表)
28
+
29
+ 这是最常见的维度表 JOIN 场景。
30
+
31
+ **Case 1:A 有增量数据,B 无变化**
32
+ ```
33
+ 增量计划:A 的变更数据 LEFT JOIN B 的全量数据
34
+ ```
35
+ - 新增的 A 行与 B 的最新数据做 LEFT JOIN
36
+ - 如果 JOIN 上 → 输出完整行
37
+ - 如果没 JOIN 上 → B 侧输出 NULL
38
+ - ✅ 结果正确
39
+
40
+ **Case 2:B 有数据变更,A 无变化**
41
+ ```
42
+ 增量计划:不触发计算(变更数据为空)
43
+ ```
44
+ - B 的变更被完全忽略
45
+ - 之前 A 行没 JOIN 上 B 输出的 `(xxx, NULL)` 不会被修正为 `(xxx, yyy)`
46
+ - 之前 A 行 JOIN 上的旧 B 数据不会被更新为新值
47
+ - ⚠️ 结果与全量重算不一致,但这是**预期行为**
48
+
49
+ **Case 3:A 和 B 同时有变化**
50
+ ```
51
+ 增量计划:A 的变更数据 LEFT JOIN B 的全量数据
52
+ ```
53
+ - 只处理 A 的增量,B 的变更被忽略
54
+ - 新增的 A 行会 JOIN 到 B 的最新数据
55
+ - 但已有的 A 行不会因 B 的变更而更新
56
+ - ⚠️ 新旧数据可能存在不一致
57
+
58
+ ### A INNER JOIN B(B 为维度表)
59
+
60
+ **Case 1:A 有增量数据,B 无变化**
61
+ ```
62
+ 增量计划:A 的变更数据 INNER JOIN B 的全量数据
63
+ ```
64
+ - 新增的 A 行与 B 做 INNER JOIN
65
+ - JOIN 不上的 A 行被丢弃
66
+ - ✅ 结果正确
67
+
68
+ **Case 2:B 有数据变更,A 无变化**
69
+ ```
70
+ 增量计划:不触发计算
71
+ ```
72
+ - B 新增了能匹配已有 A 行的数据 → 不会产出新结果
73
+ - B 删除了匹配已有 A 行的数据 → 已输出的结果不会被撤回
74
+ - ⚠️ 结果与全量重算不一致
75
+
76
+ ### 多表 JOIN 中的维度表
77
+
78
+ ```sql
79
+ -- t2, t3 都是维度表
80
+ CREATE DYNAMIC TABLE dt
81
+ TBLPROPERTIES('mv_const_tables'='t2,t3')
82
+ AS
83
+ SELECT t1.*, t2.v1, t3.v1
84
+ FROM t1
85
+ LEFT JOIN t2 ON t1.id = t2.id
86
+ LEFT JOIN t3 ON t1.id = t3.id;
87
+ ```
88
+
89
+ - 只有 t1 的变更会触发增量计算
90
+ - t2、t3 的变更都被忽略
91
+ - 增量计划:t1 的变更数据 LEFT JOIN t2 的全量数据 LEFT JOIN t3 的全量数据
92
+
93
+ ## 适合使用维度表的场景
94
+
95
+ ### ✅ 推荐场景
96
+
97
+ 1. **码表/字典表 JOIN**
98
+ - 如:地区码表、产品分类表、状态码映射表
99
+ - 特点:数据量小、极少变更、即使变更也不影响历史分析
100
+ ```sql
101
+ -- 地区码表几乎不变
102
+ TBLPROPERTIES('mv_const_tables'='dim_region')
103
+ ```
104
+
105
+ 2. **T+1 维度表 + 实时事实表**
106
+ - 维度表每天批量更新一次,事实表持续写入
107
+ - 在两次维度表更新之间,维度表可视为不变
108
+ ```sql
109
+ -- 用户画像表每天更新,订单表实时写入
110
+ TBLPROPERTIES('mv_const_tables'='dim_user_profile')
111
+ ```
112
+
113
+ 3. **配置表 JOIN**
114
+ - 如:业务规则配置、阈值配置、权重配置
115
+ - 变更频率极低,且变更后可以手动触发全量刷新
116
+ ```sql
117
+ TBLPROPERTIES('mv_const_tables'='config_rules')
118
+ ```
119
+
120
+ 4. **大事实表 JOIN 小维度表,且对维度表变更的实时性要求低**
121
+ - 核心诉求是事实表的增量计算性能
122
+ - 维度表偶尔变更后,可以接受短暂的数据不一致
123
+ ```sql
124
+ -- 商品信息表偶尔更新,订单表持续写入
125
+ TBLPROPERTIES('mv_const_tables'='dim_product')
126
+ ```
127
+
128
+ 5. **不支持 time travel 的外部表作为 JOIN 右表**
129
+ - 外部表无法提供变更数据,标记为维度表后可以正常进行增量计算
130
+ - 增量引擎会读取外部表的最新快照
131
+ ```sql
132
+ -- 外部 MySQL 表不支持 time travel
133
+ TBLPROPERTIES('mv_const_tables'='external_mysql_table')
134
+ ```
135
+
136
+ ### ❌ 不推荐场景
137
+
138
+ 1. **维度表频繁更新且要求结果实时一致**
139
+ - 如:用户状态表每分钟更新,且下游报表要求实时反映最新状态
140
+ - 此时不应标记为维度表,应让两侧都参与增量计算
141
+
142
+ 2. **维度表变更会影响聚合结果的正确性**
143
+ - 如:价格表更新后,历史订单的金额计算应该用旧价格
144
+ - 但维度表标记后,新的事实行会 JOIN 到新价格,旧事实行保持旧价格
145
+ - 如果业务要求所有行统一使用最新价格,不应使用维度表
146
+
147
+ 3. **维度表数据量大且变更频繁**
148
+ - 维度表标记的优化收益来自跳过变更数据的计算
149
+ - 如果维度表本身很大且频繁变更,应该考虑让它正常参与增量
150
+
151
+ ## 维度表变更后的数据订正
152
+
153
+ 由于维度表的变更不会触发增量计算,当维度表发生了重要变更(如修正了错误数据、更新了映射关系),DT 中已有的结果不会自动更新。**如果需要订正数据,必须执行全量刷新。**
154
+
155
+ ```sql
156
+ -- 强制全量刷新(推荐)
157
+ -- set cz.optimizer.incremental.force.full.refresh=true
158
+ REFRESH DYNAMIC TABLE my_dt;
159
+ -- 刷新完成后记得关闭,否则后续每次都是全量
160
+ SET cz.optimizer.incremental.force.full.refresh = false;
161
+
162
+ -- 如果是分区表,也可以只全量刷新指定分区(在 Studio 任务中执行)
163
+ SET cz.optimizer.incremental.force.full.refresh = true;
164
+ REFRESH DYNAMIC TABLE my_dt PARTITION(ds = '2025-01-01');
165
+ SET cz.optimizer.incremental.force.full.refresh = false;
166
+ ```
167
+
168
+ 配置说明:
169
+ - `cz.optimizer.incremental.force.full.refresh`:默认 `false`。设为 `true` 后,下一次 REFRESH 会忽略增量逻辑,对所有源表做全量扫描重算
170
+ - `SET cz.optimizer.*` 配置项在交互式 SQL 中可用
171
+ - `dt.args.*` 参数仅在 Studio 任务中可用,不能在交互式 SQL 中 SET
172
+ - 该配置是 session 级别的,刷新完成后需要手动设回 `false`,否则后续所有 REFRESH 都会走全量
173
+ - backfill 模式(`cz.optimizer.incremental.backfill.enabled=TRUE`)也会自动开启全量刷新
174
+
175
+ ## 性能收益
176
+
177
+ 标记维度表后的优化效果:
178
+ - **跳过维度表的变更数据扫描**:不需要读取维度表的变更日志
179
+ - **简化增量计划**:只需要用事实表的变更数据 JOIN 维度表的全量数据,不需要反向计算
180
+
181
+ ## ⚠️ 开启维度表后可能出现的数据不一致与重复
182
+
183
+ 标记维度表是一种**用一致性换性能**的权衡。以下是具体会出现问题的场景,使用前务必评估业务是否可以接受。
184
+
185
+ ### 场景 1:LEFT JOIN 维度表更新导致 NULL 不被修正
186
+
187
+ ```sql
188
+ -- DT 定义
189
+ SELECT order.*, product.name
190
+ FROM order LEFT JOIN product ON order.pid = product.id;
191
+ -- product 标记为维度表
192
+ ```
193
+
194
+ | 时间 | 事件 | DT 中的结果 | 全量重算应有的结果 |
195
+ |------|------|------------|------------------|
196
+ | T1 | order 插入 (pid=100),product 中无 id=100 | (pid=100, name=NULL) | (pid=100, name=NULL) |
197
+ | T2 | product 插入 id=100, name='手机' | (pid=100, name=NULL) **不变** | (pid=100, name='手机') |
198
+
199
+ **原因**:product 的变更不触发增量计算,T1 输出的 NULL 行永远不会被修正。
200
+
201
+ ### 场景 2:INNER JOIN 维度表新增数据导致结果缺失
202
+
203
+ ```sql
204
+ SELECT order.*, product.name
205
+ FROM order INNER JOIN product ON order.pid = product.id;
206
+ -- product 标记为维度表
207
+ ```
208
+
209
+ | 时间 | 事件 | DT 中的结果 | 全量重算应有的结果 |
210
+ |------|------|------------|------------------|
211
+ | T1 | order 插入 (pid=200),product 中无 id=200 | 无输出(INNER JOIN 不匹配) | 无输出 |
212
+ | T2 | product 插入 id=200, name='电脑' | **仍然无输出** | (pid=200, name='电脑') |
213
+
214
+ **原因**:product 的新增不触发增量,已有的 order 行不会被重新 JOIN。
215
+
216
+ ### 场景 3:维度表删除/更新导致过期数据残留
217
+
218
+ ```sql
219
+ SELECT order.*, product.name, product.price
220
+ FROM order LEFT JOIN product ON order.pid = product.id;
221
+ -- product 标记为维度表
222
+ ```
223
+
224
+ | 时间 | 事件 | DT 中的结果 | 全量重算应有的结果 |
225
+ |------|------|------------|------------------|
226
+ | T1 | order 插入 (pid=100),product id=100 price=99 | (pid=100, price=99) | (pid=100, price=99) |
227
+ | T2 | product 更新 id=100 price=**199** | (pid=100, price=**99**) 旧值残留 | (pid=100, price=199) |
228
+ | T3 | product 删除 id=100 | (pid=100, price=**99**) 仍然残留 | (pid=100, name=NULL) |
229
+
230
+ **原因**:维度表的 UPDATE/DELETE 都被忽略,已输出的行保持旧值。
231
+
232
+ ### 场景 4:维度表 + 聚合导致聚合结果不一致
233
+
234
+ ```sql
235
+ SELECT product.category, SUM(order.amount) as total
236
+ FROM order LEFT JOIN product ON order.pid = product.id
237
+ GROUP BY product.category;
238
+ -- product 标记为维度表
239
+ ```
240
+
241
+ | 时间 | 事件 | DT 中的结果 | 全量重算应有的结果 |
242
+ |------|------|------------|------------------|
243
+ | T1 | order (pid=1, amount=100),product (id=1, category='A') | category='A', total=100 | 同左 |
244
+ | T2 | product 更新 id=1 的 category 从 'A' 改为 'B' | category='A', total=100 **不变** | category='B', total=100 |
245
+ | T3 | order 新增 (pid=1, amount=200) | category='B', total=200(新行 JOIN 到新 category)| category='B', total=300 |
246
+
247
+ **原因**:T2 的 category 变更不触发重算,T1 的旧数据仍按旧 category 聚合。T3 的新数据按新 category 聚合。最终结果中同一个 pid 的数据被分到了不同 category,聚合结果错乱。
248
+
249
+ ### 总结:什么时候结果会不一致
250
+
251
+ | 维度表变更类型 | LEFT JOIN | INNER JOIN |
252
+ |--------------|-----------|------------|
253
+ | 新增匹配行 | 旧 fact 行的 NULL 不被修正 | 旧 fact 行不会产出新结果 |
254
+ | 更新已有行 | 旧 fact 行保持旧值 | 旧 fact 行保持旧值 |
255
+ | 删除已有行 | 旧 fact 行保持旧值(不会变 NULL) | 旧 fact 行不会被撤回 |
256
+
257
+ **核心原则**:维度表的任何变更都不会影响已经输出的结果行。只有新的事实表增量才会 JOIN 到维度表的最新快照。