foxtail463 commented on PR #67182:
URL: https://github.com/apache/doris/pull/67182#issuecomment-5505611868
# 单调函数谓词存储裁剪性能 A/B 测试报告
## 一、结论
本次单机 A-B-A 测试得到三类结果:
1. **数据范围清晰且谓词有选择性时,收益非常明显。**
- `date_trunc` 查询 P50 降低 84.3%,加速 6.38 倍;
- `substring` 查询 P50 降低 88.6%,加速 8.77 倍;
- 19/20 个 Segment 被 Zone Map 排除,实际扫描行数减少 95%。
2. **Segment 范围互相重叠时,无法做 Segment 裁剪,但简单裸列范围仍可能提前过滤行。**
- 混合日期数据没有过滤任何 Segment,也没有减少 ScanBytes;
- 但裸列 `dt` 范围先过滤了 95% 的行,避免对这些行执行昂贵的 `date_trunc`;
- P50 仍降低约 42.4%,加速 1.74 倍。
3. **无法裁剪或没有选择性时,没有确定性执行收益。**
- 所有数据都满足推导范围时,P50 变化约 1%,属于噪声;
- 不支持推导的谓词不会改变执行计划和 Profile,只有十几微秒级 Planner 检查成本。
## 二、公共测试设置
### 数据规模
```text
单节点、单 Bucket OLAP 表
1,000,000 行
```
每种模式、每条 SQL:
```text
预热 5 次(控制场景为 3 次)
正式测量 20 次(控制场景为 10 次)
使用同一 MySQL 连接
统计 P50、P95
```
## 三、可强力加速的场景:范围清晰且选择性高
### 数据布局
20 个 Segment 分别保存不同日期和不同字符串前缀:
```text
Segment 00:2026-01-01,p00_...
Segment 01:2026-01-02,p01_...
...
Segment 19:2026-01-20,p19_...
```
每个 Segment 的 `dt` 和 `v` min/max 互不重叠。
### 查询一:`date_trunc`
```sql
SELECT SUM(LENGTH(payload))
FROM monotonic_pruning_ab.bench
WHERE date_trunc(dt, 'day') = '2026-01-10 00:00:00';
```
### 查询二:字符串前缀
```sql
SELECT SUM(LENGTH(payload))
FROM monotonic_pruning_ab.bench
WHERE substring(v, 1, 3) = 'p09';
```
两种模式返回结果均为:
```text
25,600,000
```
### 延迟结果
| 查询 | Baseline A1 P50 | PR P50 | Baseline A2 P50 | Baseline 平均 P50 | P50 降低
| 加速 |
|---|---:|---:|---:|---:|---:|---:|
| `date_trunc` | 1376.8 ms | **217.5 ms** | 1400.7 ms | 1388.8 ms |
**84.3%** | **6.38x** |
| `substring` | 2378.0 ms | **274.6 ms** | 2441.0 ms | 2409.5 ms | **88.6%**
| **8.77x** |
| 查询 | Baseline 平均 P95 | PR P95 | P95 降低 | 加速 |
|---|---:|---:|---:|---:|
| `date_trunc` | 1788.5 ms | **272.1 ms** | **84.8%** | **6.57x** |
| `substring` | 2978.3 ms | **317.1 ms** | **89.4%** | **9.39x** |
两轮 Baseline P50 分别只相差 1.7% 和 2.6%,B 的巨大提升不是缓存或时间漂移造成的。
### Profile 结果:`date_trunc`
| 指标 | Baseline A1 | PR | Baseline A2 |
|---|---:|---:|---:|
| Segment 过滤数 | 0 / 20 | **19 / 20** | 0 / 20 |
| ScanRows | 1,000,000 | **50,000** | 1,000,000 |
| RowsStatsFiltered | 0 | **950,000** | 0 |
| RowsExprPredFiltered | 950,000 | **0** | 950,000 |
| ScanBytes | 7.85 MB | **588.93 KB** | 7.85 MB |
| ScannerCpuTime | 1.203 s | **126.8 ms** | 1.351 s |
| ExprFilterEvalTime | 1.000 s | **55.3 ms** | 1.126 s |
### Profile 结果:`substring`
| 指标 | Baseline A1 | PR | Baseline A2 |
|---|---:|---:|---:|
| Segment 过滤数 | 0 / 20 | **19 / 20** | 0 / 20 |
| ScanRows | 1,000,000 | **50,000** | 1,000,000 |
| RowsStatsFiltered | 0 | **950,000** | 0 |
| RowsExprPredFiltered | 950,000 | **0** | 950,000 |
| ScanBytes | 16.73 MB | **1.02 MB** | 16.73 MB |
| ScannerCpuTime | 2.329 s | **172.3 ms** | 2.292 s |
| ExprFilterEvalTime | 1.915 s | **96.1 ms** | 1.901 s |
## 四、Segment 无法裁剪,但仍可能加速
为了验证数据布局影响,另建一张 `bench_mixed`:每个 Segment 都混合包含全部 20 天的数据。
因此每个 Segment 的日期 min/max 都覆盖:
```text
2026-01-01 ~ 2026-01-20
```
同样查询单日:
```sql
SELECT SUM(LENGTH(payload))
FROM monotonic_pruning_ab.bench_mixed
WHERE date_trunc(dt, 'day') = '2026-01-10 00:00:00';
```
### 结果
| 指标 | Baseline | PR |
|---|---:|---:|
| Segment 过滤数 | 0 / 20 | 0 / 20 |
| ScanRows | 1,000,000 | 1,000,000 |
| ScanBytes | 11.50 MB | 11.50 MB |
| RowsExprPredFiltered | 950,000 | 0 |
| RowsVectorPredFiltered | 0 | 950,000 |
| ExprFilterEvalTime | 约 0.80~0.93 s | **79.1 ms** |
| ScannerCpuTime | 约 1.06~1.24 s | **618.4 ms** |
| P50 | 约 1228.3 ms | **707.2 ms** |
即使 Zone Map 无法排除 Segment,推导出的简单 `dt >= ... AND dt < ...` 仍能先用向量化比较过滤 95%
的行,再对剩余行执行 `date_trunc`,本次 P50 降低约 **42.4%**。
这类收益依赖 BE 的谓词执行顺序和函数成本,不能等同于存储级 I/O 裁剪收益。
## 五、没有明显加速的场景
### 1. 推导范围没有选择性
查询:
```sql
SELECT SUM(LENGTH(payload))
FROM monotonic_pruning_ab.bench
WHERE date_trunc(dt, 'year') = '2026-01-01 00:00:00';
```
表中全部数据都属于 2026 年,推导出的范围为:
```sql
dt >= '2026-01-01 00:00:00'
AND dt < '2027-01-01 00:00:00'
```
但所有 Segment、所有行都满足该范围。
| 指标 | Baseline | PR |
|---|---:|---:|
| Segment 过滤数 | 0 / 20 | 0 / 20 |
| ScanRows | 1,000,000 | 1,000,000 |
| ScanBytes | 11.50 MB | 11.50 MB |
| P50 | 约 1954.0 ms | 1934.7 ms |
变化约 1%,属于测试噪声。结论是:**函数能够推导,不代表一定能够裁剪;范围必须有选择性。**
### 2. 不支持推导的谓词
控制查询:
```sql
SELECT SUM(LENGTH(payload))
FROM monotonic_pruning_ab.bench
WHERE id % 20 = 9;
```
该谓词不属于 prefix、year、date format 或 rounding 推导族,因此开启 PR 前后计划完全相同。
| 指标 | Baseline | PR |
|---|---:|---:|
| Segment 过滤数 | 0 / 20 | 0 / 20 |
| ScanRows | 1,000,000 | 1,000,000 |
| ScanBytes | 11.63 MB | 11.63 MB |
| RowsExprPredFiltered | 950,000 | 950,000 |
| P50 | 约 1230.7 ms | 1281.1 ms |
本轮观察到约 4.1% 的耗时上浮,但执行计划和全部 Scan Profile 指标都没有变化,代码路径上仅增加 FE Planner
中约十几微秒的类型检查,因此不能将这 50 ms 归因于本 PR;它属于本地执行抖动。对这类查询应预期为**没有执行收益,只有很小的 Planner
遍历成本**。
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]