FAQ:常见疑问

VictoriaMetrics 和 Prometheus 是什么关系?

>VM 是 Prometheus 的长期存储后端,不是替代品。Prometheus 仍然负责抓取(scraping)和告警评估(alerting),数据通过 remote_write 协议写入 VictoriaMetrics。VM 提供了比 Prometheus 原生存储更高的扩展性和更低的资源占用。

Q2. 为什么 VictoriaMetrics 能比 Prometheus 省 7x RAM?

>来自三大设计的协同:MergeSet LSM-less + TSIDCache 37% + blockCache 三层。Prometheus 把所有索引和数据块 mmap 到内存,而 VM 只把热点数据(TSIDCache + blockCache)保留在内存,冷数据放在磁盘按需读取。

Q3. Single-Node 和 Cluster 模式有什么区别?什么时候用哪个?

>生产用 Cluster(支撑 100 万~10 亿 series),Single-Node 仅用于快速验证。Single-Node 是单进程,部署简单;Cluster 模式把 vminsert/vmstorage/vmselect 分离部署,支持水平扩展和多租户。

Q4. VictoriaMetrics 支持多租户吗?

>Cluster 模式原生支持多租户,通过 accountID/projectID 实现隔离。每个租户的数据在存储层完全隔离,适合 SaaS 或内部多业务线监控平台。

Q5. MergeSet 和 LSM Tree 有什么区别?

MergeSet 采用 LSM-less 设计:只合并不分层。LSM Tree(如 RocksDB)有 L0→L1→L2→... 多层合并,开销大;MergeSet 只有 Small Parts 和 Big Parts 两类,查询时只需读最新的 Big Part + 最近的 Small Parts。

Q6. 什么是 commonPrefix 压缩?为什么能节省 30-50% 空间?

**commonPrefix 压缩利用标签值的共同前缀来减少存储。**例如:metric{job="prometheus",instance="localhost:9090"} 和 metric{job="prometheus",instance="localhost:9091"} 共享 metric{job="prometheus",instance="localhost:909"} 前缀,只需存储一次。

Q7. TSIDCache 37% 是怎么来的?

>37% 是经验值,经过大量测试发现这个比例能最大化缓存命中率。TSIDCache 全称 Time Series ID Cache,缓存「metricName→metricID」映射,是写入和查询的关键路径。37% 的比例在 cache hit rate 和 memory usage 之间取得了最优平衡。详见 #170 TSIDCache 37% 计算:内存分配逻辑。

Q8. BloomFilter 在 VictoriaMetrics 中起什么作用?

>BloomFilter 用于基数限制(cardinality limiting),防止高 cardinality 数据打爆 VM。每个小时和每天的 BloomFilter 会记录见过的 metricIDs,如果某小时 metricID 数量超过限制,会拒绝写入。

Q9. VictoriaMetrics 有 WAL(Write-Ahead Log)吗?

**没有!这是 VM 的设计选择。**Prometheus TSDB 使用 WAL 来保证崩溃恢复,但 WAL 会增加写入延迟。VM 通过 InMemoryPart 每 2 秒刷盘的设计(pendingRowsFlushInterval),在保证数据安全的同时避免了 WAL 的开销。

Q10. InMemoryPart 刷盘失败会丢数据吗?

**正常情况下不会丢数据。**InMemoryPart 在内存中维护多个副本,刷盘时会先写临时文件再原子重命名。如果进程崩溃,正在刷盘的数据可能丢失,但这是 Prometheus remote_write 协议允许的"最多一次"语义范围内。

Q11. Cardinality 爆炸的根本原因是什么?

**高 cardinality 来自标签值组合过多。**例如:metric{job="foo",instance="ip-1"}、metric{job="foo",instance="ip-2"}... 每个 instance 都是一个唯一的 time series。如果有 1 万个 instance,就是 1 万个 series。

Q12. 如何诊断 slow query?

**使用 QueryTracer 分析查询各阶段耗时。**VM 的 QueryTracer 会在查询的每个阶段记录耗时:parse → plan → execution → merge。打开 debug 日志可以看到详细的阶段信息。

Q13. blockCache 三层(ibCache/idxbCache/ibSparseCache)分别缓存什么?

>ibCache 缓存数据块(items.bin),idxbCache 缓存索引块(index.bin),ibSparseCache 是 ibCache 的稀疏访问优化。三者合计占用 memory.Allowed() 的 40%,加上 10% 的 metricNameCache、约 3% 的 tagFiltersCache 等共同瓜分 memory.Allowed()

Q14. 如何规划 VictoriaMetrics 的内存容量?

没有统一的拆分公式,VM 在启动时由 memory.Allowed() 统一核算后分配给多个独立缓存池(TSIDCache / blockCache / metricNameCache / tagFiltersCache / dateMetricIDCache 等)。日常只需重点关注 vm_slow_row_inserts_totalvm_cache_size_bytesvm_cache_misses_total 这几项指标;命中率下降或缓存溢出时按需调整对应缓存大小或节点规格。100 万 series 起步建议预留 4--8GB 内存做基线,再根据实际命中率与慢插入比例动态扩容。

Q15. retention 设置后数据会立即删除吗?

**不会立即删除,数据会在后台分多层异步清理。**VM 的清理机制分三层:分区级默认每 1 分钟左右巡视一次过期的月份分区目录,Part 级默认每 7 分钟左右巡视一次过期 Part 文件,Block 级则在每次合并时按需裁剪过期样本。磁盘空间释放可能有延迟,这是设计如此,并非异常。

VictoriaMetrics 的 retention 清理采用三层异步清理机制,源码实现非常精妙:

15.1 三层清理机制概览
复制代码
  Retention 清理三层架构
  │
  ├── [Layer 1] 分区级清理 (table.retentionWatcher)
  │   ├── 触发周期: ~1 分钟 (+ 随机抖动)
  │   ├── 职责: 删除整个过期分区目录
  │   └── 源码: lib/storage/table.go:428-467
  │
  ├── [Layer 2] Part 级清理 (partition.stalePartsRemover)
  │   ├── 触发周期: ~7 分钟 (+ 随机抖动)
  │   ├── 职责: 标记过期 Part 并在 Merge 时删除
  │   └── 源码: lib/storage/partition.go:1742-1754
  │
  └── [Layer 3] Block 级裁剪 (mergeBlocks)
      ├── 触发时机: 每次 Merge 时
      ├── 职责: Block 内过期样本的精确裁剪
      └── 源码: lib/storage/merge.go:85-89, 205-216
15.2 分区级清理 (table.retentionWatcher)

lib/storage/table.go:428-467

分区是按月组织的(202306、202307 等),当整个分区超过 retention 期限时,整个分区目录会被删除:

复制代码
  func (tb *table) retentionWatcher() {
    d := timeutil.AddJitterToDuration(time.Minute)
    ticker := time.NewTicker(d)
    for {
        select {
        case 
15.3 Part 级清理 (partition.stalePartsRemover)

lib/storage/partition.go:1742-1754(stalePartsRemover 主体)

Part 是 MergeSet 存储的基本单元(包含 metaindex/index/items/lens 四个文件)。每个 Part 独立检查是否过期:

复制代码
  // stalePartsRemover 每 ~7 分钟运行一次
func (pt *partition) stalePartsRemover() {
    d := timeutil.AddJitterToDuration(7 * time.Minute)
    ticker := time.NewTicker(d)
    for {
        select {
        case 
15.4 Block 内样本级裁剪 (mergeBlocks)

lib/storage/merge.go:85-89 与 205-216

即使 Part 没有完全过期,在每次 Merge 过程中也会裁剪过期的样本:

复制代码
  // mergeBlockStreamsInternal 中
for bsm.NextBlock() {
    b := bsm.Block
    
    // 跳过整个 Block 都在 retention 之外的情况
    retentionDeadline := bsm.getRetentionDeadline(&b.bh)
    if b.bh.MaxTimestamp < retentionDeadline {
        localRowsDeleted += uint64(b.bh.RowsCount)
        continue  // 跳过这个 Block
    }
    
    // 合并时裁剪 Block 内过期的样本
    mergeBlocks(tmpBlock, pendingBlock, b, retentionDeadline, &localRowsDeleted)
}

// mergeBlocks 中调用 skipSamplesOutsideRetention
func skipSamplesOutsideRetention(b *Block, retentionDeadline int64, rowsDeleted *uint64) {
    if b.bh.MinTimestamp >= retentionDeadline {
        return  // 快速路径:Block 内所有样本都在 retention 范围内
    }
    // 遍历时间戳,跳过 < retentionDeadline 的样本
    for nextIdx < len(timestamps) && timestamps[nextIdx] < retentionDeadline {
        nextIdx++
    }
    if n := nextIdx - nextIdxOrig; n > 0 {
        *rowsDeleted += uint64(n)
        b.nextIdx = nextIdx  // 裁剪掉过期样本
    }
}
15.5 清理时机总结表
清理层级 触发周期 清理对象 源码位置
分区级 (table) ~1 分钟 整个月份分区目录 table.go:428-467
Part 级 (partition) ~7 分钟 过期 Part 文件 partition.go:1742-1754
Block 级 (merge) 每次 Merge 时 Block 内的过期样本 merge.go:85-89, 205-216
15.6 为什么不是"立即删除"?

清理被分散到多层、多周期、带随机抖动地执行,原因是:

  • 分区级:~1 分钟检查一次过期月份目录
  • Part 级:~7 分钟检查一次过期 Part
相关推荐
辰烨chenye43 分钟前
LeetCode Hot 100 题解 · 普通数组篇
算法·leetcode·职场和发展
mldong2 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排3 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60613 小时前
如何自己造一个时间处理库
前端·javascript·github
lhldsg4 小时前
全民健身解决方案小程序开发:从0到1的技术实战
java·数据库·需求分析
jason成都4 小时前
Spring WebFlux 适配达梦新方案|dm‑r2dbc:Netty 异步传输的实验性 R2DBC 驱动
java·后端·spring
泡海椒4 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
hqyjzsb4 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
辰烨chenye4 小时前
LeetCode Hot 100 题解 · 二分篇
java·算法·leetcode
微功夫信息技术4 小时前
分层多智能体强化学习驱动的非急救转运公平 - 效率统一调度系统研究与实践
人工智能·学习·算法·动态规划