TimescaleDB 函数 timescaledb_create_upper_paths_hook分析

参数说明

参数 类型 含义
input_rel RelOptInfo* 输入集合------即当前 stage 的上层关系(UpperRelation)的输入。例如对于 UPPERREL_GROUP_AGG 阶段,input_rel 就是 UPPERREL_PARTIAL_GROUP_AGG(或 UPPERREL_GROUP_AGG 本身,取决于 PG 版本)。它包含了聚合操作需要处理的所有行/元组信息。
output_rel RelOptInfo* 输出集合------即当前 stage 要构建的目标上层关系。TimescaleDB 在这里往 output_rel->pathlist 中添加自定义的 Path(如 gapfill path、chunkwise aggregation path、skip-scan path 等)。

timescaledb_create_upper_paths_hook 各段分类

① 链式调用 prev hook(第 1648-1649 行)

if (prev_create_upper_paths_hook != NULL)

prev_create_upper_paths_hook(root, stage, input_rel, output_rel, extra);

语义必须。 这是 PostgreSQL 的 hook 链式调用惯例。如果跳过,其他扩展注册的 create_upper_paths_hook 就不会被执行。


②replace_hypertable_modify_paths(第 1659-1662 行)

if (output_rel->pathlist != NIL)

output_rel->pathlist = replace_hypertable_modify_paths(root, output_rel->pathlist, input_rel);

语义必须。 看 replace_hypertable_modify_paths 的逻辑(第 1586-1637 行):当 output_rel 的 pathlist 包含 ModifyTablePath 且操作是 INSERT/UPDATE/DELETE

时,它会用 TimescaleDB 自定义的 HypertableModifyPath 替换掉。

这个自定义 Path 的作用是将 DML 路由到正确的 chunk(见函数上方注释第 1531-1583 行的图解:插入 ChunkDispatch + HypertableModify

节点)。如果不做这个替换,INSERT/UPDATE/DELETE 会直接写入 hypertable 本身而不是分散到各个 chunk,结果完全错误。


③ts_plan_process_partialize_agg(第 1664-1670 行)

if (parse->hasAggs && stage == UPPERREL_GROUP_AGG)

partials_found = ts_plan_process_partialize_agg(root, output_rel);

条件语义必须。 只有当用户 SQL 中显式调用了 _timescaledb_functions.partialize_agg() 时才必须执行。如果 query 里没有这个函数,它直接返回 false 什么都不改。

这个函数响应的是用户显式调用了 _timescaledb_functions.partialize_agg() 这个函数。看它的行为:

  1. 扫描 query 的 targetList,查找是否有 partialize_agg(...) 包裹的聚合函数调用

  2. 如果有,修改 AggPath 的 aggsplit 标志,把普通聚合(AGGSPLIT_SIMPLE)改成部分聚合(AGGSPLIT_INITIAL_SERIAL),或者把 finalize 阶段改成 combine

阶段(AGGSPLIT_FINAL_DESERIAL →AGGSPLITOP_COMBINE \| ... \| AGGSPLITOP_SKIPFINAL)

  1. 如果没有 combine 路径,还二次遍历 targetList 做实际修改(TS_FIX_AGGSPLIT_SIMPLE)

这直接影响查询结果的计算方式:

  • 不用 partialize_agg() →正常算出最终聚合结果

  • 用了 partialize_agg(avg(temp)) →只算到部分聚合阶段,返回的是中间序列化状态而不是最终值

所以这不是可选的性能优化,而是对用户 SQL 中显式语义表达的实现。

如果你不打算支持 partialize_agg 这个函数,可以整个跳过这段。


④ts_preprocess_first_last_aggregates(第 1676 行)

if (parse->hasAggs)

ts_preprocess_first_last_aggregates(root, root->processed_tlist);

纯优化。 这是为 FIRST()/LAST() 聚合生成子查询 + index scan 路径,替代全表扫描聚合。不调用结果一样正确,只是慢一些。


⑤ts_plan_add_hashagg(第 1679-1680 行)

if (!partials_found)

ts_plan_add_hashagg(root, input_rel, output_rel);

纯优化。 添加 hash aggregation 路径作为备选,供规划器按成本选择。不走这条路径就用标准 sort-based agg,结果正确。


⑥TSL hook 分发(第 1683-1685 行)

if (ts_cm_functions->create_upper_paths_hook != NULL)

ts_cm_functions->create_upper_paths_hook(...);

TSL 功能 是否语义必须
gapfill(plan_add_gapfill) ---给 time_bucket() 等 gapfill 函数添加自定义 Path 纯优化
chunkwise aggregation(tsl_pushdown_partial_agg) ---chunkwise 聚合下推,把部分聚合推到每个 chunk 上减少数据传输 纯优化
window gapfill adjustment ---调整 gapfill 的窗口函数 targetlist 纯优化
skip-scan(tsl_skip_scan_paths_add) ---给 SKIP SCAN 添加自定义 Path 纯优化

这些 hook 做的事情都是往 output_rel->pathlist 里追加额外的 Path 供规划器选最优成本路径。如果不调用:

  • 查询结果不变(语义一致)

  • 只是少了 gapfill、chunkwise agg、skip-scan 这几条优化路径,规划器会走标准的 PostgreSQL 聚合/DISTINCT 路径

  • 性能可能差一些,但功能不受影响

相关推荐
杜子不疼.1 天前
AI直接“上手“数据库:电科金仓KES MCP Server落地实操
时序数据库
TDengine (老段)2 天前
TDengine JOIN 完整语法 — Inner/Outer/ASOF/Window 全语法详解
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
似璟如你2 天前
IoTDB 从零入门:安装、部署与第一个时序数据写入(Windows版本)
apache·时序数据库·iotdb
TDengine (老段)3 天前
TDengine 函数完整参考 — 聚合、时序、字符串、时间、数学
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
TDengine (老段)3 天前
昆仑数智选择 TDengine TSDB,提升页岩气生产运行可视化与精细化管理能力
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
万物工业软件3 天前
中铁四局・天陇铁路智慧梁场智能化监控管理系统
时序数据库
测试开发Kevin3 天前
在电商系统中,哪些数据需要放到缓存?哪些数据需要放到DB永存呢?
时序数据库·数据库架构
TDengine (老段)4 天前
TDengine DML SELECT — 完整查询语法参考
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
Database_Cool_4 天前
时序数据库选型:阿里云 Lindorm 时序引擎 vs InfluxDB 全维度对比
数据库·阿里云·时序数据库