查询历史数据时,总要等 S3 或者 OSS 慢悠悠地返回索引文件——这是用对象存储甩不掉的痛。GreptimeDB 1.0.0-beta.1 引入了索引文件独立缓存,专门解决这个问题。
新版本把索引文件单独缓存在本地磁盘,启动时自动从对象存储预加载,历史数据查询不再频繁访问对象存储,延迟大幅降低。
为什么需要独立缓存索引文件?
先看 GreptimeDB 的存储架构。使用对象存储时,数据写入流程如下:
写入请求 → WAL → Memtable → Flush → Parquet 文件
↓
Write Cache(本地磁盘)
↓
对象存储(S3/GCS/Azure Blob)查询时,GreptimeDB 会尽量从本地缓存读取。缓存层次从快到慢:
- Memtable — 最新写入,纯内存
- Write Cache — 近期写入数据的本地缓存
- Read Cache — 查询时从对象存储加载的数据,LRU 淘汰
- 对象存储 — 持久化存储,延迟最高
索引文件独立缓存是 Write Cache 的一部分,后续版本中 Write Cache 和 Read Cache 将统一。
问题在于:之前的版本中,增大本地缓存只对新写入的数据文件有效。历史数据的索引文件没有专门的缓存空间,每次查询都可能访问对象存储。
更麻烦的是,索引文件体积小但访问频率极高——每次查询都要先读索引来定位数据。当索引和大体积数据文件共用缓存池时,索引很容易被挤出去。
工作原理
1.0.0-beta.1 的方案很直接:给索引文件一个专属缓存空间,不再与数据文件抢地盘。
具体机制:
- 独立缓存池:Parquet(数据文件)和 Puffin(索引文件)使用两个独立的 LRU 缓存池,互不干扰
- 默认 20%:自动从 Write Cache 划出 20% 给索引,每个池至少 512MB
- 启动时预加载:后台异步从对象存储拉取索引到本地,按时间戳降序加载,最新数据优先;超过缓存容量时停止,不影响启动速度
- 可调比例:通过
index_cache_percent自定义分配
实际效果:
- 索引不会被大数据文件挤出缓存
- 历史索引常驻本地,不用等首次查询触发加载
- 重启后快速恢复,没有"热机"过程
配置方法
基础配置
使用对象存储时,Write Cache 默认开启,只需设置索引占比:
[[region_engine]]
[region_engine.mito]
# 对象存储场景默认开启
enable_write_cache = true
# Write Cache 总大小
write_cache_size = "10GiB"
# 索引文件占比(默认 20,有效范围 1-99)
index_cache_percent = 20
# 启动时预加载索引(默认 true)
preload_index_cache = true这样配置后,10GiB 的 Write Cache 分 2GiB 给索引,剩余 8GiB 用于数据文件。
调整比例
调高比例的场景:
- 索引缓存命中率低于 80%
- 通过
information_schema.tables查看index_length,索引总大小超过当前缓存分配 - 历史查询占比高
- 查询条件复杂,频繁访问索引
调低比例的场景:
- 主要查最近的数据(热数据在 Write Cache 中)
- 表结构简单,索引文件本身不大
- 本地磁盘空间紧张
监控缓存效果
用 Prometheus 指标观察缓存命中情况,type 标签区分索引和数据文件:
# 索引缓存未命中率
rate(greptime_mito_cache_miss{type="index"}[5m])
# 数据文件缓存未命中率
rate(greptime_mito_cache_miss{type="file"}[5m])
# 索引缓存使用量
greptime_mito_cache_bytes{type="index"}
# 索引缓存命中率
rate(greptime_mito_cache_hit{type="index"}[5m]) /
(rate(greptime_mito_cache_hit{type="index"}[5m]) + rate(greptime_mito_cache_miss{type="index"}[5m]))如果索引缓存命中率低于 80%(经验值),说明缓存容量可能不足,可以增大 write_cache_size 或调高 index_cache_percent。
与其他缓存的关系
GreptimeDB 有多层缓存,了解它们的分工有助于做出更好的配置:
[[region_engine]]
[region_engine.mito]
# 磁盘级缓存
enable_write_cache = true
write_cache_size = "10GiB"
index_cache_percent = 20
# 内存级缓存
sst_meta_cache_size = "128MB"
vector_cache_size = "512MB" # Tag 列 Arrow Array
page_cache_size = "512MB"
selector_result_cache_size = "512MB" # 文件 Last Row 缓存
# 索引专用内存缓存
[region_engine.mito.index]
metadata_cache_size = "64MiB"
content_cache_size = "128MiB"各层缓存的分工:
| 缓存 | 位置 | 作用 |
|---|---|---|
| Write Cache(数据部分) | 磁盘 | 避免从对象存储下载数据文件 |
| Write Cache(索引部分) | 磁盘 | 避免从对象存储下载索引文件 |
metadata_cache_size | 内存 | 避免从磁盘读取索引元数据 |
content_cache_size | 内存 | 避免从磁盘读取索引内容 |
磁盘缓存和内存缓存是互补的:磁盘缓存决定"要不要从对象存储下载",内存缓存决定"要不要从本地磁盘读取"。磁盘空间充足时增大 write_cache_size,内存充足时增大 metadata_cache_size 和 content_cache_size。
最佳实践
场景一:Trace 链路查询
某用户将 100 亿条 Trace 数据存储在阿里云 OSS,需要根据 trace_id 进行单条链路的全局查询。通过 information_schema.tables 查询 index_length,索引总大小约 106GB。升级前,由于索引频繁被挤出缓存,查询经常超时或耗时很长。启用独立索引缓存后,查询基本都能在分钟内返回。
推荐配置:
[[region_engine]]
[region_engine.mito]
write_cache_size = "200GiB"
index_cache_percent = 30 # 约 60GB 索引缓存,覆盖热点索引
[region_engine.mito.index]
metadata_cache_size = "128MiB"
content_cache_size = "256MiB"场景二:实时监控为主
主要查最近数据的监控面板场景,默认配置即可:
[[region_engine]]
[region_engine.mito]
write_cache_size = "10GiB"
index_cache_percent = 20升级注意事项
从旧版本升级到 1.0.0-beta.1:
- 无需手动迁移:功能开箱即用,启动后自动在后台加载索引
- 留意启动期 I/O:首次启动可能有短暂的 I/O 高峰(后台预加载),可通过
greptime_mito_cache_fill_downloaded_files指标监控预加载进度 - 检查磁盘空间:确保本地磁盘有足够空间
- 如需关闭预加载:设置
preload_index_cache = false - 集群部署:每个 Datanode 都需要配置,缓存不在节点间共享
小结
索引文件独立缓存是 GreptimeDB 针对对象存储场景的关键优化:默认分配 20% 的 Write Cache 给索引,启动时后台预加载(最新数据优先),通过 index_cache_percent 灵活调整。
升级后历史查询响应会有明显改善。
配置详情参考:GreptimeDB 性能调优指南



