文件压缩
IEDB 的自动压缩系统将小的 Parquet 文件合并成更大的、优化的文件,从而显著加快查询速度。
概述
压缩是 IEDB 的文件优化系统,它将小文件合并成大文件,从而将查询性能提高 10-50 倍。
主要特点:
- 自动- 按计划运行(默认:每小时 05:05)
- 安全- 锁定分区可防止并发压缩
- 高效- 使用高性能算子引擎进行快速并行合并
- 非阻塞式- 查询在压缩期间也能正常工作
- 默认启用- 生产环境必备
信息 默认情况下启用压缩功能,并每小时自动运行一次。
为什么压缩很重要
小文件问题
IEDB 的高性能数据写入(每秒 947 万条记录)会生成许多小文件:
每秒 947 万条记录,每 5 秒刷新一次:
→ 每 5 秒产生 1000 万条记录
→ 每个测量值每分钟产生 12 个文件
→ 每个测量值每小时产生 720 个文件
→ 每个测量值每天产生 17,280 个文件
对查询的影响:
- 查询速度慢——分析引擎必须打开/扫描数百个文件
- 成本高昂——需要更多 S3/MinIO API 调用
- 压缩效果差——小文件压缩效率较低。
- 减少修剪——分区消除效果降低
压缩后
实际生产测试结果:
合并前:2,704 个小文件(Snappy 压缩)= 3.7 GB
合并后:3 个合并文件(ZSTD 压缩)= 724 MB
压缩率:节省 80.4% 的存储空间
文件数量:减少至原来的 1/901(从 2,704 个减少到 3 个)
合并耗时:5 秒
按测量项目细分:
- 内存:888 个文件 → 1 个文件,1,213 MB → 239 MB(压缩率 80.3%)
- 磁盘:906 个文件 → 1 个文件,1,237 MB → 242 MB(压缩率 80.4%)
- CPU:910 个文件 → 1 个文件,1,246 MB → 243 MB(压缩率 80.5%)
查询性能:
- 速度提升 10-50 倍——单文件扫描对比数百个文件扫描。
- API 调用次数减少 99% - 成本大幅降低(2704 次 LIST 操作 → 3 次)
- 压缩率达 80.4% - ZSTD 压缩与 Snappy 写入对比
- 有效的剪枝——分析引擎可以跳过整个文件
工作原理
压缩流程
调度器启动(定时任务:每小时的第5分钟)
↓
扫描存储,查找符合条件的可合并分区
↓
对每个分区:检查分区时长(是否超过1小时?)检查文件数量(是否≥10个?)检查是否已经合并过?
↓
获取分区锁(基于 SQLite)
↓
将小文件下载到临时目录
↓
使用算子引擎进行合并(并行处理、排序)
↓
将合并后的文件上传回存储
↓
删除旧的原始小文件
↓
释放分区锁并清理临时文件
↓
继续处理下一个分区
分区结构
数据按小时整理:
iedb/ # 存储桶(S3 bucket 或本地根目录)
├── default/ # 数据库名称
│ └── cpu/ # 测量值(表名)
│ └── 2025/10/08/ # 日期分区(按天)
│ ├── 14/ # 小时分区(14点 = 下午2点)
│ │ ├── file1.parquet
│ │ ├── file2.parquet
│ │ └── ...
│ ├── 15/ # 15点
│ └── 16/ # 16点
压缩将分区中的所有文件(例如,2025/10/08/14/)合并成一个优化后的文件。
配置
默认配置
默认情况下,压缩功能已启用toml:
[compaction]
enabled = true
min_age_hours = 1 # 等待1小时后再合并(确保当前小时数据完整)
min_files = 10 # 仅当文件数量≥10时才进行合并
target_file_size_mb = 512 # 合并后文件的目标大小(单位:MB)
schedule = "5 * * * *" # Cron 表达式:每小时的第5分钟执行
max_concurrent_jobs = 2 # 并行执行的合并任务数(最多2个)
compression = "zstd" # 使用 zstd 压缩(比 snappy 压缩率更高)
compression_level = 3 # 压缩级别(平衡压缩率与速度)
配置选项
定时任务
[compaction]
schedule = "5 * * * *" # 每小时的第5分钟执行(默认值)
# schedule = "0 */2 * * *" # 每2小时的第0分钟执行
# schedule = "0 2 * * *" # 每天凌晨2点执行
Cron 格式: minute hour day month weekday
最低时限
[compaction]
min_age_hours = 1 # 不合并当前小时的数据(默认值)
# min_age_hours = 2 # 等待2小时后再合并(更保守)
# min_age_hours = 0 # 立即合并(激进)
警告 设置min_age_hours = 0可以在数据仍在写入时压缩当前小时,可能会创建许多压缩文件。
最小文件
[compaction]
min_files = 10 # 仅当文件数量 ≥ 10 时才进行合并(默认值)
# min_files = 50 # 仅在文件数量较多时合并(更保守)
# min_files = 5 # 更激进地合并(阈值更低)
目标文件大小
[compaction]
target_file_size_mb = 512 # 目标文件大小 512MB(默认值)
# target_file_size_mb = 1024 # 更大的文件(文件数更少,合并时间更长)
# target_file_size_mb = 256 # 更小的文件(文件数更多,合并速度更快)
并发配置
[compaction]
max_concurrent_jobs = 2 # 并行执行 2 个合并任务(默认值)
# max_concurrent_jobs = 4 # 更高并行度(消耗更多 CPU/内存)
# max_concurrent_jobs = 1 # 串行执行(资源占用更低)
压缩
[compaction]
compression = "zstd" # 最佳压缩率(默认值)
compression_level = 3 # 平衡压缩速度与压缩率(默认值)
# 可选值:
# compression = "snappy" # 速度最快,压缩率较低
# compression = "gzip" # 压缩率较好,速度较慢
# compression = "zstd" # 压缩率最佳,速度良好
禁用压缩
[compaction]
enabled = false
何时禁用:
-
测试写入性能
-
写入量极低(< 10 个文件/小时)
-
调试压缩问题
警告 禁用文件压缩会导致查询速度显著降低,因为文件会不断累积。
监测
检查压缩状态
curl http://localhost:8000/api/compaction/status \
-H "Authorization: Bearer YOUR_TOKEN"
响应:
{
"enabled": true,
"running": false,
"last_run": "2025-10-08T14:05:00Z",
"next_run": "2025-10-08T15:05:00Z",
"stats": {
"total_jobs": 42,
"successful_jobs": 40,
"failed_jobs": 2,
"total_files_compacted": 12580,
"total_bytes_saved": 8589934592
}
}
获取详细统计数据
curl http://localhost:8000/api/compaction/stats \
-H "Authorization: Bearer YOUR_TOKEN"
列出符合条件的分区
curl http://localhost:8000/api/compaction/candidates \
-H "Authorization: Bearer YOUR_TOKEN"
响应:
{
"candidates": [
{
"partition": "default/cpu/2025/10/08/14",
"file_count": 150,
"total_size_mb": 7500,
"age_hours": 2.5,
"eligible": true
},
{
"partition": "default/mem/2025/10/08/14",
"file_count": 120,
"total_size_mb": 6000,
"age_hours": 2.5,
"eligible": true
}
],
"total_candidates": 2
}
手动触发
curl -X POST http://localhost:8000/api/compaction/trigger \
-H "Authorization: Bearer YOUR_TOKEN"
查看压缩进程
curl http://localhost:8000/api/compaction/jobs \
-H "Authorization: Bearer YOUR_TOKEN"
查看压缩历史
curl http://localhost:8000/api/compaction/history \
-H "Authorization: Bearer YOUR_TOKEN"
性能影响
压缩性能
**测试环境:**苹果 M3 Max(14 核,48GB 内存)
| 文件 | 尺寸 | 压实时间 | 最终尺寸 | 压缩 |
|---|---|---|---|---|
| 888 | 1.2 GB | 2.1秒 | 239 MB | 80.3% |
| 906 | 1.2 GB | 2.2秒 | 242 MB | 80.4% |
| 910 | 1.2 GB | 2.3秒 | 243 MB | 80.5% |
总计: 2704 个文件(3.7 GB)→ 3 个文件(724 MB),耗时6.6 秒
查询性能
压缩前:
SELECT * FROM default.cpu WHERE time > NOW() - INTERVAL 1 HOUR;
-- 5.2 seconds (scan 720 files)
压缩后:
SELECT * FROM default.cpu WHERE time > NOW() - INTERVAL 1 HOUR;
-- 0.05 seconds (scan 1 file) - 104x faster!
节省存储空间
Original files (Snappy): 3.7 GB
Compacted files (ZSTD): 724 MB
Space saved: 80.4%
最佳实践
1. 自动运行压缩
默认的定时计划(每小时一次)适用于大多数使用场景:
[compaction]
enabled = true
schedule = "5 * * * *"
2. 监测压缩进程
设置以下提醒:
- 压缩进程失败
- 包含超过 1000 个文件的分区
- 耗时超过 10 分钟
3. 根据写入量进行调整
高容量(>1000万条记录/秒):
[compaction]
min_files = 100 # 等待更多文件后再合并(提高触发门槛)
max_concurrent_jobs = 4 # 更高并行度(最多同时运行4个合并任务)
低容量(<100K 条记录/秒):
[compaction]
min_files = 5 # 文件数量较少时即触发合并(阈值更低)
schedule = "0 */6 * * *" # 每6小时执行一次
4. 使用合适的目标文件大小
[compaction]
target_file_size_mb = 512 # 合适的默认值
# target_file_size_mb = 1024 # 适用于非常大的数据集
# target_file_size_mb = 256 # 用于更快的合并
5. 从源头减少文件生成
**最佳实践:**增加缓冲区大小以减少生成的文件数量:
[ingest]
max_buffer_size = 200000 # 从 50,000 上调(文件数量减少至原来的 1/4)
max_buffer_age_ms = 10000 # 从 5000 上调(文件数量减少至原来的 1/2)
影响:
- 文件生成量:2000个/小时 → 250个/小时(减少8倍)
- 压缩时间:150秒 → 20秒(速度提升7倍)
- 内存使用量:每个工作线程 +300MB
这是最有效的优化方法——文件越少,压缩速度越快,查询速度也越快。
故障排除
压缩未运行
查看状态:
curl http://localhost:8000/api/compaction/status
验证配置:
# Check if enabled
grep "enabled" iedb.toml
# Check schedule
grep "schedule" iedb.toml
查看日志:
# Docker
docker logs iedb | grep compaction
# Native
sudo journalctl -u iedb | grep compaction
压缩时间过长
**问题:**运行时间超过 30 分钟
解决方案:
- 减小目标文件大小:
[compaction]
target_file_size_mb = 256 # 较小块
- 提高并发:
[compaction]
max_concurrent_jobs = 4
- 从源头减少文件:
[ingest]
max_buffer_size = 200000
压缩过程中磁盘空间不足
**症状:**磁盘空间不足导致压缩失败
解决方案:
- 在较大的磁盘上使用临时目录:
export TMPDIR=/mnt/large-disk/tmp
- 减少并发量:
[compaction]
max_concurrent_jobs = 1
- 手动清理旧的压缩文件:
# 删除已合并的小文件
find ./data -name "*.parquet" -size -10M -delete
锁无法释放
**问题:**分区卡在“锁定”状态
检查锁:
# 查看锁
sqlite3 ./data/iedb.db "SELECT * FROM compaction_locks;"
清除陈旧锁芯:
# 超过两小时自动处理或者手动处理
sqlite3 ./data/iedb.db "DELETE FROM compaction_locks WHERE expires_at < datetime('now');"
API 参考
GET /api/v1/compaction/status
获取当前压缩状态。
回复:
{
"enabled": true,
"running": false,
"last_run": "2025-10-08T14:05:00Z",
"next_run": "2025-10-08T15:05:00Z"
}
GET /api/v1/compaction/stats
获取详细的压缩统计数据。
GET /api/v1/compaction/candidates
列出符合压缩条件的分区。
POST /api/v1/compaction/trigger
手动触发。
响应:
{
"message": "Compaction triggered",
"job_id": "comp_1696775400"
}
GET /api/v1/compaction/jobs
查看正在进行的压缩作业。
GET /api/v1/compaction/history
查看压缩进程记录。
概括
压缩对于生产环境部署至关重要:
好处:
- 查询速度提升 10-50 倍
- 节省 80% 的存储空间
- API 调用次数减少 99%。
- 自动且安全
默认配置适用于大多数情况:
[compaction]
enabled = true
schedule = "5 * * * *"
min_age_hours = 1
min_files = 10
定期监测:
- 查看
/api/v1/compaction/status - 失败进程警报
- 注意那些文件数超过 1000 个的分区。