预聚合(Pre-Aggregation)是一种通过预先计算聚合结果来加速查询的技术。系统会在查询时自动选择合适的预聚合表,无需修改查询代码。
用户查询 → 匹配预聚合 → 重写 SQL → 查询预聚合表 → 返回结果
↓
无匹配时
↓
查询原始表
在 TM 文件中添加 preAggregations 配置:
export const model = {
name: 'FactSalesModel',
tableName: 'fact_sales',
dimensions: [...],
measures: [...],
// 预聚合配置
preAggregations: [
{
name: 'daily_product_sales',
caption: '按产品日销售汇总',
tableName: 'preagg_daily_product_sales',
priority: 80,
dimensions: ['salesDate', 'product'],
granularity: {
salesDate: 'day'
},
dimensionProperties: {
salesDate: ['year', 'quarter', 'month'],
product: ['category_name', 'brand']
},
measures: [
{ name: 'quantity', aggregation: 'SUM' },
{ name: 'salesAmount', aggregation: 'SUM' },
{ name: 'orderCount', aggregation: 'COUNT' }
],
refresh: {
strategy: 'INCREMENTAL',
schedule: '0 2 * * *',
watermarkColumn: 'salesDate$id',
lookbackDays: 3
},
enabled: true
}
]
};
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | 预聚合名称(唯一标识) |
caption |
string | 否 | 显示名称 |
tableName |
string | 是 | 预聚合表名 |
schema |
string | 否 | 数据库 schema |
priority |
int | 否 | 优先级 1-100,默认 50 |
enabled |
boolean | 否 | 是否启用,默认 true |
| 参数 | 类型 | 说明 |
|---|---|---|
dimensions |
string[] | 包含的维度名称列表 |
granularity |
object | 时间维度的粒度配置 |
dimensionProperties |
object | 各维度包含的属性 |
时间粒度选项:
| 粒度 | 说明 | 示例 |
|---|---|---|
minute |
分钟 | 2024-01-15 10:30 |
hour |
小时 | 2024-01-15 10:00 |
day |
天 | 2024-01-15 |
week |
周 | 2024-W03 |
month |
月 | 2024-01 |
quarter |
季度 | 2024-Q1 |
year |
年 | 2024 |
measures: [
{ name: 'quantity', aggregation: 'SUM' },
{ name: 'salesAmount', aggregation: 'SUM' },
{ name: 'orderCount', aggregation: 'COUNT' },
{ name: 'minPrice', aggregation: 'MIN' },
{ name: 'maxPrice', aggregation: 'MAX' }
]
支持的聚合类型:
| 聚合 | 说明 | 可 Rollup |
|---|---|---|
SUM |
求和 | 是 |
COUNT |
计数 | 是(转为 SUM) |
MIN |
最小值 | 是 |
MAX |
最大值 | 是 |
AVG |
平均值 | 需要 SUM+COUNT |
refresh: {
strategy: 'INCREMENTAL', // FULL | INCREMENTAL
schedule: '0 2 * * *', // Cron 表达式
watermarkColumn: 'salesDate$id',
lookbackDays: 3
}
| 参数 | 类型 | 说明 |
|---|---|---|
strategy |
string | 刷新策略:FULL(全量)、INCREMENTAL(增量) |
schedule |
string | Cron 表达式 |
watermarkColumn |
string | 水位线列(增量刷新使用) |
lookbackDays |
int | 回溯天数(处理延迟数据) |
可以为预聚合配置永久过滤条件:
filters: [
{ column: 'orderStatus', operator: 'eq', value: 'COMPLETED' },
{ column: 'salesDate', operator: 'gte', value: '2023-01-01' }
]
系统根据以下规则自动匹配预聚合:
查询的维度必须是预聚合维度的子集。
查询: [salesDate, product]
预聚合: [salesDate, product, customer]
结果: 匹配 ✓
查询的维度属性必须在预聚合中存在。
查询: product$category_name
预聚合 dimensionProperties: { product: ['category_name', 'brand'] }
结果: 匹配 ✓
注意:caption 和 id 是隐式属性,无需显式配置。
查询粒度必须 >= 预聚合粒度(支持向上聚合)。
预聚合粒度: day
查询 salesDate$month: 匹配 ✓ (day → month 可 rollup)
查询 salesDate$hour: 不匹配 ✗ (day → hour 无法细化)
查询的度量必须在预聚合中存在,且聚合方式兼容。
当多个预聚合都满足条件时,按以下顺序选择:
priority 值更高根据配置手动创建预聚合表:
CREATE TABLE preagg_daily_product_sales (
-- 维度列
sales_date DATE NOT NULL,
product_id BIGINT NOT NULL,
product_category_name VARCHAR(100),
product_brand VARCHAR(100),
-- 度量列
quantity_sum DECIMAL(20,4),
sales_amount_sum DECIMAL(20,4),
order_count BIGINT,
-- 元数据列
_preagg_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
_preagg_updated_at TIMESTAMP,
PRIMARY KEY (sales_date, product_id)
);
CREATE INDEX idx_preagg_daily_sales_date
ON preagg_daily_product_sales(sales_date);
POST /api/pre-agg/{modelName}/{preAggName}/create-table
配置 refresh.schedule 后,系统会按 Cron 表达式自动刷新。
POST /api/pre-agg/{modelName}/{preAggName}/refresh
全量刷新 (FULL):
TRUNCATE TABLE preagg_daily_product_sales;
INSERT INTO preagg_daily_product_sales (...)
SELECT DATE(order_date), product_id, SUM(quantity), ...
FROM fact_sales
LEFT JOIN dim_product ON ...
GROUP BY DATE(order_date), product_id;
增量刷新 (INCREMENTAL):
-- 删除受影响的数据(含回溯)
DELETE FROM preagg_daily_product_sales
WHERE sales_date >= '2024-01-08';
-- 插入新数据
INSERT INTO preagg_daily_product_sales (...)
SELECT ... FROM fact_sales
WHERE order_date >= '2024-01-08'
GROUP BY ...;
当预聚合数据可能不完整时(如增量刷新场景),系统会自动启用混合查询:
预聚合数据 (watermark 之前)
UNION ALL
原始表数据 (watermark 之后)
系统通过 watermarkColumn 和 dataWatermark 判断数据时效性:
查询结果的 extData 会包含预聚合使用信息:
{
"data": [...],
"extData": {
"preAggUsed": "daily_product_sales",
"preAggMode": "direct", // direct | rollup | hybrid
"originalSql": "..."
}
}
| 端点 | 方法 | 说明 |
|---|---|---|
/api/pre-agg/list |
GET | 列出所有预聚合 |
/api/pre-agg/{model}/{name}/status |
GET | 获取状态 |
/api/pre-agg/{model}/{name}/refresh |
POST | 手动刷新 |
为同一数据源配置多个粒度的预聚合:
preAggregations: [
{
name: 'daily_sales',
tableName: 'preagg_daily_sales',
priority: 80,
dimensions: ['salesDate', 'product'],
granularity: { salesDate: 'day' },
measures: [...]
},
{
name: 'monthly_sales',
tableName: 'preagg_monthly_sales',
priority: 60,
dimensions: ['salesDate', 'product'],
granularity: { salesDate: 'month' },
measures: [...]
}
]
为不同的维度组合配置预聚合:
preAggregations: [
{
name: 'product_sales',
tableName: 'preagg_product_sales',
priority: 70,
dimensions: ['salesDate', 'product'],
...
},
{
name: 'customer_sales',
tableName: 'preagg_customer_sales',
priority: 70,
dimensions: ['salesDate', 'customer'],
...
},
{
name: 'channel_sales',
tableName: 'preagg_channel_sales',
priority: 70,
dimensions: ['salesDate', 'channel'],
...
}
]
在配置预聚合前,分析实际的查询模式:
定期检查预聚合的刷新状态,确保数据时效性。
预聚合 + 缓存可实现最佳性能:
检查以下几点:
enabled 是否为 true可能原因:
lookbackDays 设置过小目前不支持在查询中强制指定。可以通过:
priority 影响匹配顺序enabled: false 禁用特定预聚合