Skip to content
On this page
教程
2025-12-02

让历史数据查询更快:GreptimeDB 索引文件独立缓存指南

GreptimeDB 1.0.0-beta.1 引入了索引文件独立缓存,专门解决对象存储的延迟问题。新版本把索引文件单独缓存在本地磁盘,启动时自动从对象存储预加载,历史数据查询不再频繁访问对象存储,延迟大幅降低。

查询历史数据时,总要等 S3 或者 OSS 慢悠悠地返回索引文件——这是用对象存储甩不掉的痛。GreptimeDB 1.0.0-beta.1 引入了索引文件独立缓存,专门解决这个问题。

新版本把索引文件单独缓存在本地磁盘,启动时自动从对象存储预加载,历史数据查询不再频繁访问对象存储,延迟大幅降低。

为什么需要独立缓存索引文件?

先看 GreptimeDB 的存储架构。使用对象存储时,数据写入流程如下:

写入请求 → WAL → Memtable → Flush → Parquet 文件

                              Write Cache(本地磁盘)

                              对象存储(S3/GCS/Azure Blob)

查询时,GreptimeDB 会尽量从本地缓存读取。缓存层次从快到慢:

  1. Memtable — 最新写入,纯内存
  2. Write Cache — 近期写入数据的本地缓存
  3. Read Cache — 查询时从对象存储加载的数据,LRU 淘汰
  4. 对象存储 — 持久化存储,延迟最高

索引文件独立缓存是 Write Cache 的一部分,后续版本中 Write Cache 和 Read Cache 将统一。

问题在于:之前的版本中,增大本地缓存只对新写入的数据文件有效。历史数据的索引文件没有专门的缓存空间,每次查询都可能访问对象存储。

更麻烦的是,索引文件体积小但访问频率极高——每次查询都要先读索引来定位数据。当索引和大体积数据文件共用缓存池时,索引很容易被挤出去。

工作原理

1.0.0-beta.1 的方案很直接:给索引文件一个专属缓存空间,不再与数据文件抢地盘。

具体机制:

  • 独立缓存池:Parquet(数据文件)和 Puffin(索引文件)使用两个独立的 LRU 缓存池,互不干扰
  • 默认 20%:自动从 Write Cache 划出 20% 给索引,每个池至少 512MB
  • 启动时预加载:后台异步从对象存储拉取索引到本地,按时间戳降序加载,最新数据优先;超过缓存容量时停止,不影响启动速度
  • 可调比例:通过 index_cache_percent 自定义分配

实际效果:

  1. 索引不会被大数据文件挤出缓存
  2. 历史索引常驻本地,不用等首次查询触发加载
  3. 重启后快速恢复,没有"热机"过程

配置方法

基础配置

使用对象存储时,Write Cache 默认开启,只需设置索引占比:

toml
[[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 标签区分索引和数据文件:

promql
# 索引缓存未命中率
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 有多层缓存,了解它们的分工有助于做出更好的配置:

toml
[[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_sizecontent_cache_size

最佳实践

场景一:Trace 链路查询

某用户将 100 亿条 Trace 数据存储在阿里云 OSS,需要根据 trace_id 进行单条链路的全局查询。通过 information_schema.tables 查询 index_length,索引总大小约 106GB。升级前,由于索引频繁被挤出缓存,查询经常超时或耗时很长。启用独立索引缓存后,查询基本都能在分钟内返回。

推荐配置:

toml
[[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"

场景二:实时监控为主

主要查最近数据的监控面板场景,默认配置即可:

toml
[[region_engine]]
[region_engine.mito]
write_cache_size = "10GiB"
index_cache_percent = 20

升级注意事项

从旧版本升级到 1.0.0-beta.1:

  1. 无需手动迁移:功能开箱即用,启动后自动在后台加载索引
  2. 留意启动期 I/O:首次启动可能有短暂的 I/O 高峰(后台预加载),可通过 greptime_mito_cache_fill_downloaded_files 指标监控预加载进度
  3. 检查磁盘空间:确保本地磁盘有足够空间
  4. 如需关闭预加载:设置 preload_index_cache = false
  5. 集群部署:每个 Datanode 都需要配置,缓存不在节点间共享

小结

索引文件独立缓存是 GreptimeDB 针对对象存储场景的关键优化:默认分配 20% 的 Write Cache 给索引,启动时后台预加载(最新数据优先),通过 index_cache_percent 灵活调整。

升级后历史查询响应会有明显改善。

配置详情参考:GreptimeDB 性能调优指南

加入我们的社区

获取 Greptime 最新更新,并与其他用户讨论。