本文说明 Foggy Java 引擎中的预聚合能力。该能力已有模型定义、匹配、SQL 改写和测试覆盖,但在具体项目中是否启用,应结合真实数据规模、刷新链路、查询口径和性能收益做项目级验证。预聚合是查询性能优化能力,不改变 TM/QM 的字段语义,也不是 MCP 工具协议的一部分。
preAggregations 用于在 TM 上声明已经存在或可被刷新任务生成的预计算聚合表。查询执行时,引擎会根据当前查询的维度、时间粒度、指标、过滤条件和运行时配置,判断是否可以把原始聚合查询改写到预聚合表上执行。
在完成项目级验证后,它适合以下场景:
它不适合替代完整的语义建模,也不应默认对所有查询开启。字段是否可查询仍由 QM 决定;预聚合只决定一次查询能否走更快的物理执行路径。
公开文档中应按以下口径理解预聚合:
| 层面 | 状态 |
|---|---|
| 引擎实现 | Java 引擎包含预聚合定义加载、匹配、SQL 改写、rollup、hybrid 和 returnTotal 聚合路径 |
| 自动刷新 | 存在独立 add-on,可生成 DDL 和全量/增量刷新 SQL,并提供刷新服务入口 |
| 测试覆盖 | 已有匹配、集成、数据一致性、边界场景和刷新 SQL 生成相关测试 |
| 项目实践 | 需要在目标项目中结合真实业务模型、数据量、刷新策略和查询证据单独验证 |
因此,预聚合在当前文档中定位为“高级可选优化能力”,而不是默认查询路径或核心语义层契约。
| 层级 | 作用 |
|---|---|
| TM | 声明物理表、字段、维度、指标,以及可选的 preAggregations 元数据 |
| QM | 决定哪些字段对查询调用方可见 |
| JSON Query DSL | 按 QM 暴露字段提交查询请求 |
| 预聚合匹配器 | 判断当前 DSL 查询生成的聚合计划是否可由某张预聚合表满足 |
| SQL 改写器 | 在命中时把查询 SQL 改写到预聚合表 |
调用方不需要在 DSL 中显式指定预聚合表。是否命中预聚合由引擎根据模型定义和运行时配置决定。
preAggregations: [
{
name: 'dailyProductSales',
caption: '每日商品销售预聚合',
tableName: 'preagg_daily_product_sales',
priority: 80,
dimensions: ['salesDate', 'product'],
granularity: {
salesDate: 'day'
},
dimensionProperties: {
product: ['caption', 'categoryName', 'brand']
},
measures: [
{ name: 'salesAmount', aggregation: 'sum', columnName: 'sales_amount' },
{ name: 'quantity', aggregation: 'sum', columnName: 'quantity' },
{ name: 'orderCount', aggregation: 'count', columnName: 'order_count' }
],
refresh: {
strategy: 'INCREMENTAL',
watermarkColumn: 'salesDate$id',
lookbackDays: 1
},
enabled: true
}
]
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
name |
string | 是 | 无 | 预聚合定义名称 |
caption |
string | 否 | 无 | 显示名称 |
tableName |
string | 是 | 无 | 预聚合物理表名 |
schema |
string | 否 | 主表 schema 或数据源默认 schema | 预聚合表 schema |
priority |
number | 否 | 50 |
匹配优先级,数值越高越优先 |
dimensions |
string[] | 否 | 无 | 预聚合表覆盖的维度 |
granularity |
object | 否 | 无 | 时间维度粒度映射 |
dimensionProperties |
object | 否 | 无 | 预聚合表覆盖的维度属性 |
dimensionPropertyColumnNames |
object | 否 | 无 | 维度属性到预聚合表物理列的映射 |
measures |
array | 否 | 无 | 预聚合表覆盖的指标 |
filters |
array | 否 | 无 | 预聚合表内置过滤条件元数据 |
refresh |
object | 否 | 无 | 刷新策略元数据 |
enabled |
boolean | 否 | true |
是否启用 |
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | 对应 TM/QM 指标名 |
aggregation |
string | 是 | 预聚合列所代表的聚合方式 |
columnName |
string | 否 | 预聚合表中的物理列名 |
当前匹配逻辑主要覆盖 sum、count、min、max 等可安全 rollup 的聚合。avg 需要 sum + count 才能稳定上卷,当前不应作为预聚合 rollup 的默认假设。
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
strategy |
string | 否 | FULL |
刷新策略,常见值为 FULL、INCREMENTAL |
schedule |
string | 否 | 无 | 调度表达式或宿主约定的调度标识 |
watermarkColumn |
string | 否 | 无 | 增量刷新或 hybrid 查询使用的水位字段 |
lookbackDays |
number | 否 | 无 | 增量刷新回看天数 |
刷新元数据本身不会让表自动存在。实际项目需要由工程侧创建预聚合表,并通过刷新任务、调度服务或外部数据作业维护其中的数据。
预聚合命中需要同时满足以下条件:
| 条件 | 规则 |
|---|---|
| 查询类型 | 主要面向有维度和指标的聚合查询 |
| 维度 | 查询维度必须是预聚合维度的子集 |
| 维度属性 | 查询使用的维度属性必须被预聚合覆盖;id、caption 存在部分隐式映射规则 |
| 时间粒度 | 查询粒度不能比预聚合粒度更细,例如日预聚合可以上卷到月,月预聚合不能回答日查询 |
| 指标 | 查询指标必须被预聚合覆盖 |
| 聚合方式 | 查询聚合方式必须与预聚合列兼容 |
| slice 条件 | 过滤字段需要能映射到预聚合表字段 |
| 自定义 SQL | 包含自定义 SQL 条件的查询不会命中预聚合 |
| 启用状态 | enabled: false 的预聚合不会参与匹配 |
当多张预聚合表都可满足查询时,引擎会结合 priority、维度覆盖差异和粒度差异选择更合适的表。
| 模式 | 说明 |
|---|---|
direct |
查询粒度和字段集合可直接由预聚合表满足 |
rollup |
预聚合表粒度更细,引擎在预聚合结果上继续聚合 |
hybrid |
预聚合数据覆盖历史区间,源表补充水位线之后的新数据 |
hybrid 查询依赖水位字段和运行时配置,默认不应假定开启。开启后仍需要验证水位线、刷新周期和源表补偿区间是否符合业务口径。
命中预聚合后,执行上下文会记录预聚合使用情况。宿主系统可以在查询证据或调试信息中暴露这些字段:
| 字段 | 说明 |
|---|---|
preAggUsed |
使用的预聚合名称 |
preAggMode |
命中模式,如 direct、rollup、hybrid |
preAggNeedsRollup |
是否需要在预聚合表上继续上卷 |
preAggAggregateUsed |
returnTotal 聚合查询是否使用预聚合 |
这些字段用于解释查询计划和排查性能问题,不应被当作业务结果正确性的唯一证明。重要分析仍应结合源记录、SQL/DSL 证据和业务口径复核。
Foggy Java 项目中存在独立的预聚合 add-on,用于生成预聚合表 DDL、全量刷新 SQL、增量刷新 SQL,并提供刷新服务、调度和管理接口。该能力通常由宿主系统按部署环境接入。
项目中可以按两类方式维护预聚合表:
复杂维度属性、跨表属性、特殊方言 SQL 和事务边界需要在目标运行时验证。预聚合刷新链路属于工程集成能力,不应只依赖模型文件声明。
returnTotal 聚合路径可能单独使用预聚合。avg 类指标不应默认依赖预聚合上卷。filters 字段可作为预聚合元数据声明,但生产使用前应验证内置过滤条件与查询过滤条件的兼容性。priority,但仍应通过查询证据验证实际命中情况。