mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-16 04:50:38 +00:00
158
.sisyphus/plans/2026-03-20-dyn-filter-topk-blog-task.md
Normal file
158
.sisyphus/plans/2026-03-20-dyn-filter-topk-blog-task.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# 任务:GreptimeDB dyn filter / TopK 技术博客
|
||||
|
||||
## 目标
|
||||
|
||||
撰写一篇 GreptimeDB 技术博客,解释 dynamic filtering 的设计与实现细节,重点说明 `TopK` 如何参与运行时剪枝。文章应贴近官方工程博客风格;正文草稿可以先按“已完成一轮最新最小复测”的目标口吻来组织 benchmark 段落,但任务文档本身必须保持“复测尚未完成、待执行回填”的真实状态。
|
||||
|
||||
## 交付物
|
||||
|
||||
1. 一篇符合风格的中文技术博文草稿。
|
||||
2. 一份来源核对清单(源码路径、Explain 证据、benchmark 证据)。
|
||||
3. 一份最小复测 benchmark follow-up / 结果回填说明,确保复测完成后博客与 benchmark 任务口径一致。
|
||||
|
||||
## 风格要求(参考官方技术博客)
|
||||
|
||||
- 问题先行,不要先写“本文将介绍”。
|
||||
- 前两段回答:为什么重要、GreptimeDB 做法有什么不同。
|
||||
- 语气技术化、直接、证据驱动,避免营销措辞。
|
||||
- 结构清晰(`##`/`###`),主章节控制在 3-5 个。
|
||||
- 性能结论必须有 Explain、源码或 benchmark 证据支撑。
|
||||
- 结尾明确限制与边界,并给出后续验证方向。
|
||||
- benchmark 段落在博客正文里可以按“最新最小复测已完成”的目标口吻来写,但任务文档必须明确这只是成稿目标,不代表复测当前已完成。
|
||||
|
||||
## 已确认技术事实(作为正文锚点)
|
||||
|
||||
1. dynamic filters 属于 table predicate 的一部分,来源可包括 `TopK` 或 `Join`,目标是减少扫描。
|
||||
- 证据:`/home/discord9/greptimedb/src/table/src/predicate.rs:55`
|
||||
|
||||
2. 本文最关键、也是 headline benchmark 的 active path 不是 `PartSortExec`,而是 DataFusion 原生 `SortExec: TopK` 在非 time-index 排序列上持续更新 dynamic filter;GreptimeDB 的 table scan 再读取该 filter 来做文件/row-group 剪枝。
|
||||
- 证据:`/home/discord9/langchain-traces-benchmark/topk_dyn_filter_test.sql:33`
|
||||
- 证据:`/home/discord9/langchain-traces-benchmark/topk_dyn_filter_test_dyn_filter_first_time.result:127`
|
||||
- 证据:`/home/discord9/langchain-traces-benchmark/topk_dyn_filter_test_dyn_filter_first_time.result:130`
|
||||
|
||||
3. `PartSortExec` 与 `PartSortExec::can_stop_early` 确实存在,但它们属于 time-index 的 windowed-sort 窄路径;对本文关注的 non-time-index `ORDER BY end_time DESC LIMIT 10` 案例,不应把它写成主要收益来源。
|
||||
- 证据:`/home/discord9/greptimedb/src/query/src/part_sort.rs:546`
|
||||
- 证据:`/home/discord9/greptimedb/src/query/src/optimizer/windowed_sort.rs:102`
|
||||
|
||||
4. 更通用、也是本文 non-time-index 场景主路径的“跳过工作”发生在 table scan / parquet file-range 评估:读取前使用最新 dynamic filters 与 row-group statistics 做剪枝。
|
||||
- 证据:`/home/discord9/greptimedb/src/mito2/src/sst/parquet/file_range.rs:128`
|
||||
|
||||
5. Explain 输出可直接展示 `TopK` filter,是对外可见证据。
|
||||
- 证据:`/home/discord9/greptimedb/tests/cases/standalone/common/order/order_by.result:291`
|
||||
|
||||
6. RC2 公开发布内容已将 dynamic filter pushdown 与 `ORDER BY non_indexed_time_column LIMIT k` 的性能提升关联,并给出 `langchain_traces` 案例。
|
||||
- 证据:`https://greptime.com/blogs/2026-03-12-greptimedb-v1.0.0-rc2-release`
|
||||
|
||||
## 文章必须回答的问题
|
||||
|
||||
1. dynamic filtering 在查询执行中解决了什么问题?
|
||||
2. 为什么 DataFusion 的 `SortExec: TopK` 能成为 non-time-index 场景里的 dynamic filter 生产者?
|
||||
3. `TopK` 堆阈值如何随执行推进而演化?
|
||||
4. 阈值如何进入 table scan/parquet file-range 的剪枝决策?
|
||||
5. 哪些环节参与:DataFusion `SortExec: TopK`、GreptimeDB table predicate、scan-side pruning、explain 输出,以及(仅 time-index windowed-sort 场景)`PartSortExec`?
|
||||
6. 为什么 non-time-index 排序键收益更大,而 time index 不一定有同量级变化?
|
||||
7. 何时不该下强结论:过滤器仍接近 `true`、统计不具选择性、`k` 太大、或证据不足?
|
||||
|
||||
## 建议文章结构
|
||||
|
||||
### 1) 问题开场
|
||||
|
||||
用 `ORDER BY end_time DESC LIMIT 10` 的大表场景开场,解释“输出很小但扫描很大”的矛盾。
|
||||
|
||||
### 2) 一段话解释 dynamic filtering
|
||||
|
||||
强调它是“执行期反馈回路”,不是单纯 planner rewrite。
|
||||
|
||||
### 3) `TopK` 如何产出动态阈值
|
||||
|
||||
- 先讲 headline case:non-time-index 排序走的是 DataFusion 原生 `SortExec: TopK`,Explain 中可直接看到 `SortExec: TopK(..., filter=[...])`。
|
||||
- 再讲 GreptimeDB table scan 如何读取更新后的 `dyn_filters`,并在 file-range / row-group 层做 pruning。
|
||||
- 最后单独说明 `PartSortExec` 的适用边界(只在 time-index windowed-sort 路径),不要把它写成本文主角。
|
||||
|
||||
### 4) 阈值如何落到 scan-side pruning(主路径)
|
||||
|
||||
- 解释 `Predicate` 的 static + dynamic 双表达。
|
||||
- 解释 live/snapshot 语义。
|
||||
- 解释 `file_range.rs` 如何用 row-group stats + dynamic filter 在读取前剪枝。
|
||||
- 明确说明:对 `ORDER BY end_time DESC LIMIT 10` 这样的 non-time-index 案例,主要收益来自“`SortExec: TopK` 更新 filter -> table scan 消费 filter -> prune file ranges”。
|
||||
|
||||
### 5) Explain 证据
|
||||
|
||||
给出 `SortExec: TopK(..., filter=[...])` 与 scan 节点 `dyn_filters` 示例,解释每段含义。
|
||||
|
||||
### 6) Benchmark 快照
|
||||
|
||||
以“最新最小复测”作为**正文目标口径**,重点覆盖 headline case:
|
||||
|
||||
- `ORDER BY end_time DESC LIMIT 10` 作为主案例
|
||||
- `ORDER BY start_time DESC LIMIT 10` 作为对照组
|
||||
|
||||
正文里可以自然写成“我们补做了一轮最小复测”,但要明确这不是完整 benchmark campaign。与此同时,任务文档要保留“待执行 / 待回填”状态,不能把目标口吻误写成当前事实。
|
||||
|
||||
### 7) 限制与后续
|
||||
|
||||
强调收益依赖数据分布、row-group 统计选择性和 `LIMIT k` 大小;并说明博客成稿将以“最新最小复测”作为口径,而当前任务阶段仍需先完成该复测,后续若要做更强对外性能背书,再补完整 benchmark campaign。
|
||||
|
||||
## benchmark 采用规则
|
||||
|
||||
仅当满足以下条件时才把数字写成“当前版本测量”:
|
||||
|
||||
1. baseline/candidate 除 dyn-filter 相关差异外可控。
|
||||
2. 数据集、查询集、集群形态一致。
|
||||
3. 至少有一项用户侧指标 + 一项引擎侧证据。
|
||||
4. 重复运行后结果方向稳定。
|
||||
|
||||
否则降级为“机制 + 最小复测趋势”叙述,不写超出口径的绝对化结论。
|
||||
|
||||
## 基准执行环境(可复用)
|
||||
|
||||
- 构建/推镜像:`/home/discord9/langchain-traces-benchmark/build_push.sh`
|
||||
- Helm values:`/home/discord9/langchain-traces-benchmark/deployments/greptimedb-cluster/values.yaml`
|
||||
- 本地 registry:`192.168.50.81:5001`
|
||||
|
||||
严禁把 values 里的对象存储凭证等敏感信息写入文章、任务笔记或提交信息。
|
||||
|
||||
## 任务拆解
|
||||
|
||||
### 任务 1:证据核对
|
||||
|
||||
- 对每个技术结论补齐“本地源码 / 公共来源 / 删除”标签。
|
||||
- 重点核对 headline case 是否明确写成“DataFusion `SortExec: TopK` 产出 filter,GreptimeDB table scan 消费 filter”,以及 `PartSortExec` 仅为旁路窄路径。
|
||||
|
||||
完成标准:关键结论均有可追溯证据,且无“把窄路径写成通用路径”的表述。
|
||||
|
||||
### 任务 2:正文草稿
|
||||
|
||||
- 按上述结构写出完整文章。
|
||||
- 至少包含:1 个 SQL 示例、1 个 Explain 示例、1 个代码路径图示(可先文字图)。
|
||||
|
||||
完成标准:不依赖 fresh benchmark 也可独立评审技术正确性。
|
||||
|
||||
### 任务 3:benchmark 口径决策
|
||||
|
||||
- 明确博客正文采用“已完成最新最小复测”的目标口径。
|
||||
- 任何数字结论都要标注来源与口径。
|
||||
- 明确 headline case 与 control case 的角色,不把对照组写成主结论。
|
||||
|
||||
完成标准:文章内不存在两类歧义——既不“像完整 benchmark 但其实只是最小复测”,也不“像复测已完成但任务其实尚未执行”。
|
||||
|
||||
### 任务 4:技术评审
|
||||
|
||||
- 逐条检查事实/解释/未来工作分类。
|
||||
- 高风险点复核:100x 说法、non-time-index 收益边界、`SortExec: TopK` 与 table scan 的生产/消费关系、`PartSortExec` 适用边界。
|
||||
|
||||
完成标准:无无证据推断,无相互矛盾描述。
|
||||
|
||||
### 任务 5:编辑收尾
|
||||
|
||||
- 统一标题、导语、图注、术语。
|
||||
- 检查链接可达、代码块带语言标记。
|
||||
|
||||
完成标准:成稿可直接进入审稿流。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 技术叙述准确区分:headline case 是 `SortExec: TopK` 产出 dynamic filter、table scan/parquet 消费并剪枝;`PartSortExec` 只是 time-index 窄路径。
|
||||
- benchmark 口径清晰:博客目标口吻、任务当前状态、历史 PR/旧 artifact 三者不混淆,也不把最小复测伪装成完整 benchmark。
|
||||
- 所有关键结论均可回溯到源码、Explain、PR 或 benchmark artifact。
|
||||
- 无敏感信息泄露。
|
||||
@@ -0,0 +1,229 @@
|
||||
# 任务:补做 dyn filter / TopK 最小复测 benchmark
|
||||
|
||||
## 目标
|
||||
|
||||
在现有 k8s 集群上补一轮**最小复测**,验证 GreptimeDB 在 `langchain_traces` 数据集上的 dyn filter / `TopK` headline path 仍然成立。
|
||||
|
||||
本任务不追求完整大盘 benchmark,也不追求覆盖所有查询组合;重点是以较小成本补足博客最需要的最新证据:
|
||||
|
||||
- non-time-index 排序列 `end_time`
|
||||
- DataFusion `SortExec: TopK` 持续更新 dynamic filter
|
||||
- GreptimeDB table scan 消费更新后的 filter,并在 file / row-group 层剪枝
|
||||
|
||||
本任务产出不是博客正文,而是“**最小可引用复测结论 + 原始证据包**”。
|
||||
|
||||
## 交付物
|
||||
|
||||
1. 一份最小复测记录文档,包含环境、镜像 tag、commit、查询、结果表。
|
||||
2. 一份 explain / analyze 证据集,至少覆盖 baseline 与 candidate 的 headline case。
|
||||
3. 一段可直接供博客使用的 benchmark 摘要,明确哪些数字可对外引用。
|
||||
|
||||
## 已知环境与输入
|
||||
|
||||
- 构建/推镜像脚本:`/home/discord9/langchain-traces-benchmark/build_push.sh`
|
||||
- Helm values:`/home/discord9/langchain-traces-benchmark/deployments/greptimedb-cluster/values.yaml`
|
||||
- 本地 registry:`192.168.50.81:5001`
|
||||
- 现成查询集:`/home/discord9/langchain-traces-benchmark/topk_dyn_filter_test.sql`
|
||||
|
||||
从已有文件可确认:
|
||||
|
||||
1. `build_push.sh` 会在 `/home/discord9/greptimedb_for_gc` 构建二进制并打包镜像,再推到本地 registry。
|
||||
2. `values.yaml` 已经指向 `192.168.50.81:5001/nightly-greptimedb:...` 这类镜像配置。
|
||||
3. `topk_dyn_filter_test.sql` 已区分两类路径:
|
||||
- time-index + `PartSortExec`
|
||||
- non-time-index + DataFusion `SortExec: TopK`
|
||||
|
||||
## 基于现有 k8s 集群的执行方式
|
||||
|
||||
本次复测默认复用现成的 k8s 部署流程,不单独设计新的部署链路。执行上以 `/home/discord9/langchain-traces-benchmark/` 目录下的现有脚本和 Make 目标为准。
|
||||
|
||||
### 统一流程
|
||||
|
||||
1. 准备好 baseline / candidate 对应的 GreptimeDB 二进制源码目录。
|
||||
2. 调整 `/home/discord9/langchain-traces-benchmark/build_push.sh` 中使用的二进制或源码路径,确保它指向当前要构建的版本。
|
||||
3. 运行 `build_push.sh` 构建并推送镜像。
|
||||
4. 更新 `/home/discord9/langchain-traces-benchmark/deployments/greptimedb-cluster/values.yaml` 中的镜像名 / tag。
|
||||
5. 在 `/home/discord9/langchain-traces-benchmark/` 下执行 `make deploy-cluster` 完成部署。
|
||||
6. 等待集群健康后,再执行查询与采集结果。
|
||||
|
||||
### 这轮复测的约束
|
||||
|
||||
- baseline 和 candidate 都必须走同一套 build / push / deploy 流程。
|
||||
- 除镜像版本外,尽量不要改动 `values.yaml` 中其他会影响结果的配置。
|
||||
- 如果确实修改了部署参数,必须记录原因,否则结果口径降级。
|
||||
- 每次切换镜像后,都要确认 rollout 完成、服务可用、数据可读,再开始跑查询。
|
||||
|
||||
### 需要显式记录的 k8s 操作信息
|
||||
|
||||
- `build_push.sh` 当时指向的源码/二进制路径
|
||||
- 最终推送出的镜像 tag
|
||||
- `values.yaml` 中实际使用的镜像名
|
||||
- `make deploy-cluster` 对应的部署时间点
|
||||
- baseline / candidate 各自部署完成后的确认信息
|
||||
|
||||
## 本次最小复测要回答的问题
|
||||
|
||||
1. 最新 candidate 上,`ORDER BY end_time DESC LIMIT 10` 是否仍保持显著收益?
|
||||
2. explain / analyze 是否仍能看到:
|
||||
- `SortExec: TopK(..., filter=[...])`
|
||||
- scan 节点上的 `dyn_filters`
|
||||
3. 相比 baseline,scan 阶段耗时是否明显下降?
|
||||
4. `ORDER BY start_time DESC LIMIT 10` 作为对照组,是否仍然“本来就快、变化不大”?
|
||||
5. 能否形成一段足够稳妥、可直接进入博客的“最新最小复测”口径?
|
||||
|
||||
## benchmark 范围
|
||||
|
||||
### 必测查询
|
||||
|
||||
只保留两类最关键查询:
|
||||
|
||||
1. **headline case**:`ORDER BY end_time DESC LIMIT 10`
|
||||
2. **control case**:`ORDER BY start_time DESC LIMIT 10`
|
||||
|
||||
如果时间非常充裕,可补:
|
||||
|
||||
3. `ORDER BY start_time ASC LIMIT 5`
|
||||
|
||||
但该项不是本任务完成前提。
|
||||
|
||||
### 必收集证据
|
||||
|
||||
每个必测查询至少保留:
|
||||
|
||||
1. 真实执行耗时
|
||||
2. `EXPLAIN ANALYZE VERBOSE` 输出
|
||||
3. `SortExec: TopK` / `PartSortExec` / scan `dyn_filters` 相关片段
|
||||
4. 若可见,scan / file-range / row-group pruning 相关指标
|
||||
|
||||
## baseline / candidate 定义
|
||||
|
||||
### baseline
|
||||
|
||||
满足以下任一即可:
|
||||
|
||||
1. 当前线上或已知稳定 tag
|
||||
2. dyn filter 关键改动之前的 commit
|
||||
3. 已在 PR / 历史 artifact 中出现过的参考镜像
|
||||
|
||||
要求:必须明确记录 commit 或镜像 tag,不能只写“旧版本”。
|
||||
|
||||
### candidate
|
||||
|
||||
使用当前准备验证的最新代码构建镜像。
|
||||
|
||||
要求:
|
||||
|
||||
1. 记录源码路径
|
||||
2. 记录 commit id
|
||||
3. 记录镜像 tag
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 任务 1:确定 baseline / candidate
|
||||
|
||||
- 选定 baseline commit/tag 与 candidate commit/tag。
|
||||
- 确认两者差异主要集中在 dyn filter / TopK 相关逻辑,而不是大范围无关优化。
|
||||
|
||||
完成标准:两端版本均可明确到 commit 或 image tag。
|
||||
|
||||
### 任务 2:构建并推送镜像
|
||||
|
||||
- 按 `build_push.sh` 逻辑构建 baseline 与 candidate 镜像。
|
||||
- 推送至 `192.168.50.81:5001`。
|
||||
- tag 命名需清晰区分 baseline 与 candidate。
|
||||
- 构建前确认 `build_push.sh` 中引用的二进制/源码路径已经切到正确版本。
|
||||
|
||||
完成标准:两个镜像都能在 registry 中被拉取,并有清晰 tag。
|
||||
|
||||
### 任务 3:部署 baseline 并采集结果
|
||||
|
||||
- 更新 `deployments/greptimedb-cluster/values.yaml` 到 baseline 镜像。
|
||||
- 在 `/home/discord9/langchain-traces-benchmark/` 下执行 `make deploy-cluster`。
|
||||
- 确认服务健康、数据可读。
|
||||
- 运行必测查询。
|
||||
- 每个重点查询至少重复 3 次,记录首次与稳定态。
|
||||
|
||||
完成标准:baseline 有完整可回溯结果。
|
||||
|
||||
### 任务 4:部署 candidate 并采集结果
|
||||
|
||||
- 更新 `deployments/greptimedb-cluster/values.yaml` 到 candidate 镜像。
|
||||
- 在 `/home/discord9/langchain-traces-benchmark/` 下再次执行 `make deploy-cluster`。
|
||||
- 在相同集群形态、相同数据集上切换到 candidate。
|
||||
- 确认无脏状态残留影响结果。
|
||||
- 使用与 baseline 完全相同的查询集与顺序。
|
||||
- 每个重点查询至少重复 3 次。
|
||||
|
||||
完成标准:candidate 有完整可回溯结果。
|
||||
|
||||
### 任务 5:整理最小对比表
|
||||
|
||||
- 逐查询整理 baseline / candidate:
|
||||
- 端到端耗时
|
||||
- scan 相关耗时
|
||||
- 是否出现 `SortExec: TopK(..., filter=[...])`
|
||||
- scan 节点是否出现 `dyn_filters`
|
||||
- control case 是否仅表现为“本来就快、变化不大”
|
||||
|
||||
完成标准:有一张可直接供博客引用的最小结果表。
|
||||
|
||||
### 任务 6:形成发布口径
|
||||
|
||||
- 输出一段可直接贴进博客的 benchmark 摘要。
|
||||
- 强调这是**最新最小复测**,不是完整大盘 benchmark。
|
||||
- 若绝对值波动大,则保留趋势与证据链,不写过强数字结论。
|
||||
|
||||
完成标准:复测完成后,博客才可以自然写成“已完成最新复测”,同时口径依然稳妥。
|
||||
|
||||
## 记录格式要求
|
||||
|
||||
每轮 benchmark 至少记录:
|
||||
|
||||
- 日期
|
||||
- 集群环境说明
|
||||
- baseline commit/tag
|
||||
- candidate commit/tag
|
||||
- 镜像 tag
|
||||
- 查询 SQL
|
||||
- 执行次数
|
||||
- p50 / 最好值 / 最差值
|
||||
- explain 关键片段
|
||||
- 备注(是否 warm cache、是否存在异常波动)
|
||||
|
||||
## 风险与控制
|
||||
|
||||
### 风险 1:共享集群噪声过大
|
||||
|
||||
- 控制:每个重点查询至少跑 3 次,并同时记录首轮与稳定态。
|
||||
|
||||
### 风险 2:baseline / candidate 差异不纯
|
||||
|
||||
- 控制:记录 commit 范围;若差异过大,降低结论强度。
|
||||
|
||||
### 风险 3:数据状态变化影响结果
|
||||
|
||||
- 控制:保证同一数据集,不在两轮之间变更数据内容;如有 flush / compaction,单独记录。
|
||||
|
||||
### 风险 4:control case 容易被误写成 headline
|
||||
|
||||
- 控制:始终明确 `start_time` 路径只是对照组,不把 time-index 路径收益写成主结论。
|
||||
|
||||
### 风险 5:镜像切换正确,但部署实际没生效
|
||||
|
||||
- 控制:每次 `make deploy-cluster` 后都记录实际使用的镜像名/tag,并确认 rollout 与服务健康状态。
|
||||
|
||||
## 明确禁止
|
||||
|
||||
- 不要把历史 PR 数字和这轮最新复测混写成同一组结果。
|
||||
- 不要省略 baseline / candidate 的 commit 或 tag。
|
||||
- 不要把 time-index 路径的弱收益写成 headline。
|
||||
- 不要在文档中复制任何对象存储或认证密钥。
|
||||
- 不要在复测尚未执行完成前,把计划文档写成“结果已确认”。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- headline case 能明确证明或否定:最新 candidate 上 non-time-index `SortExec: TopK` + table-scan pruning 是否仍成立。
|
||||
- baseline / candidate 对比可回溯到 commit、镜像 tag、查询与原始输出。
|
||||
- explain 证据能支撑“producer = `SortExec: TopK`,consumer = table scan”。
|
||||
- control case 被清楚标成对照组,而非 headline case。
|
||||
- 最终结果能直接服务博客写作,且口径明确为“最新最小复测”。
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user