惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
Latest news
Latest news
G
GRAHAM CLULEY
Project Zero
Project Zero
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Webroot Blog
Webroot Blog
Help Net Security
Help Net Security
TaoSecurity Blog
TaoSecurity Blog
Hacker News: Ask HN
Hacker News: Ask HN
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
C
CXSECURITY Database RSS Feed - CXSecurity.com
V2EX - 技术
V2EX - 技术
S
Secure Thoughts
AWS News Blog
AWS News Blog
W
WeLiveSecurity
云风的 BLOG
云风的 BLOG
V
V2EX
Last Week in AI
Last Week in AI
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
Google Developers Blog
P
Palo Alto Networks Blog
A
Arctic Wolf
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
M
MIT News - Artificial intelligence
V
Visual Studio Blog
C
CERT Recently Published Vulnerability Notes
WordPress大学
WordPress大学
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Simon Willison's Weblog
Simon Willison's Weblog
PCI Perspectives
PCI Perspectives
量子位
K
Kaspersky official blog
腾讯CDC
Schneier on Security
Schneier on Security
F
Full Disclosure
S
Schneier on Security

博客园 - 左扬

VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 与其他 TSDB 对比:Prometheus/InfluxDB/Thanos/VM VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 写入吞吐/查询延迟/内存占用的数学模型 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— Goroutine 池/atomic/零拷贝/sync.Pool VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 多租户架构——accountID/projectID 与 tenant 隔离 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 版本演进:1.146.0 LTS 重大更新解析 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 整体数据流:一条监控数据的完整生命周期 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 架构演进:从 TSDB 到 MergeSet 的设计取舍 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— Single-Node vs Cluster 模式本质区别 VictoriaMetrics 1.146.0 源码【左扬精讲】—— 开篇总览 Rust 专题【左扬精讲】—— 从语法到灵魂:Ownership、Borrowing 与多语言对比 kubernetes 源码【左扬精讲】—— kube-scheduler 启动流程源码分析 Rust 专题【左扬精讲】—— 选择控制语句、运算符与格式化输出 Rust 专题【左扬精讲】—— 所有权详解 Rust 专题【左扬精讲】—— 作用域详解 Rust 专题【左扬精讲】—— 变量、常量与标量数据类型 kubernetes 源码 / Operator 专题【左扬精讲】—— Deployment Controller 源码分析:从对象创建到滚动更新 kubernetes 源码 / Operator 专题【左扬精讲】—— Operator 开发中的 Webhook:从准入控制到生产部署 Kubernetes源码 / Operator 专题【左扬精讲】—— 实现 Application Controller:从零构建生产级控制器 Kubernetes 编程 / Operator 专题【左扬精讲】—— 定义 Application 资源 + 添加自定义新 API 完整指南 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 八):内部架构与核心组件 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 八): —— 从入口到调度的全链路源码剖析(k8s v1.36.1) DeepSeek-R1 多模态 R1 / VLM-GRPO【左扬精讲】—— Qwen2-VL 微调与视觉推理强化学习实战 DeepSeek-R1 工业 RAG + 微调混合系统【左扬精讲】—— R1 系列收官之作:从 Prompt → RAG → 微调 选型决策树 DeepSeek-R1 推理时扩展【左扬精讲】—— o1 / R1 慢思考机制:Self-Consistency + ToT + PRM 详解 DeepSeek-R1 端侧 LLM 工程【左扬精讲】—— llama.cpp 调参与 Apple Silicon / 国产 NPU / Android 端侧落地全攻略 DeepSeek-R1 vLLM + k8s 生产部署【左扬精讲】—— 从单卡 7B 到 100 卡 671B MoE 集群的工业化部署实战 DeepSeek-R1 评估与系统(Evaluation & Systems)【左扬精讲】—— 从 GSM8K/MMLU 到 LLM-as-Judge 的工业级评估方法论 DeepSeek-R1 模型训练与算法【左扬精讲】—— GRPO 进阶算法:DAPO / PRIME / RLVR / PRM 四大 2025 前沿改进 DeepSeek-R1 模型训练与算法【左扬精讲】—— 数据蒸馏:用 DeepSeek-R1-671B 生成 800K 高质量 CoT 样本的完整流水线 DeepSeek-R1 优化与微调实战【左扬精讲】—— 从 R1 强化学习新范式到 GRPO 微调一站式入门 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 七):自定义插件开发实战 —— 手写一个 Score 插件并注册到集群 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 六):Scheduler Profile 与多调度器 —— 如何配置多个 profile 实现多租户、Coordinated LeaderElection Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 五):SchedulingQueue 与 QueueingHint —— 三段队列的细节、v1.36 新引入的 QueueingHint 工作机制 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 四):抢占(Preemption)算法剖析 —— DefaultPreemption 如何选 victim、PodDisruptionBudget 如何约束 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 二):内置插件逐个精读 — NodeResourcesFit / NodeAffinity / TaintToleration / PodTopologySpread / VolumeBinding / InterPodAffinity Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):调度器内置插件 逐个精读 k8s 源码级精讲(二十六):调度器内置插件逐个精读 Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):调度器内置插件精读 — NodeResourcesFit / NodeAffinity / TaintToleration / PodTopologySpread / VolumeBinding / InterPodAffinity Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):Scheduling Framework 扩展点逐个源码拆解 Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):初识调度模型、内部架构与事件驱动机制 Kubernetes 编程 / client-go 专题【左扬精讲】—— 四种客户端:为什么、怎么选、怎么用 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime、kubebuilder、operator-sdk 三大框架深度对比 Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入理解 ManagedFields 字段冲突协调机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Finalizers 深度解析:对象的生命周期与删除控制 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference 字段与级联删除机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入学习 Server-Side Apply:managedFields 替代 last-applied-configuration 的演进方向 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Annotations 与元数据体系(Operator 专题) Kubernetes 编程 / Operator 专题【左扬精讲】—— RESTMapper:把 Group / Version / Kind / Resource 四元组翻译成 REST 路径的"查字典"大师 Kubernetes 编程 / Operator 专题【左扬精讲】—— Converter 资源版本转换器 Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展:从单 Deployment 到多 Workload 的复合 Operator 演进 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference / Finalizer / 准入控制:k8s 资源生命周期的三大支柱 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime 框架内幕:从 Manager 到 Reconcile 的全栈拆解 Kubernetes 编程 / Operator 专题【左扬精讲】—— 生产级 Operator 最佳实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— application-operator Reconcile 循环源码精讲:从 client-go Informer 到 workqueue 的全链路解剖 Kubernetes 编程 / Operator 专题【左扬精讲】—— 从零搭建一个 application-operator 新项目:脚手架、API 设计与基于原生 DeploymentStatus/ServiceStatus 的状态建模 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:生产级 Controller 实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析: Controller 调试与诊断工具:从日志分析到问题定位 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:错误处理与重试机制:WorkQueue 限速器详解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Leader 选举机制:高可用控制器的必备技能 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Controller 开发模式完整实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:SharedInformerFactory 与等待缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:从认证配置到 Deployment 操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:版本对应、架构组件与组件关系 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Informer 源码深度解析:从底层原理到实战应用 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Reflector 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:ListWatcher 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Indexer 与 ThreadSafeStore 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DeltaFIFO 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:workqueue 核心原理与实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— runtime.Codec 资源编解码:serializer 与 codec 差异、编解码数据结构、codec 核心调用链路 Kubernetes 编程 / Operator 专题【左扬精讲】—— Scheme 资源注册机制全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 自定义资源的内部版本与外部版本:从源码看版本定义机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 1.36.1 核心 API 数据结构全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 构建过程 【AIOPS】一文读懂LLM【左扬精讲】:从诞生到普及,解锁大语言模型的核心密码 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Hooks 源码精讲:Hooks 可观测性的无侵入式实现 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 配置解析源码精讲 ——SRE 自定义 Agent 核心技巧 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 并发模型解析 ——SRE 应对高并发采集的调优思路 【AIOPS】AI Agent 专题【左扬精讲】基础架构篇:MCP-VictoriaMetrics Golang 源码整体架构拆解 ——SRE 必懂的核心模块与数据流 OpenTelemetry 开发实战【左扬精讲】—— 云原生可观测体系构建与分布式追踪二次开发 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(下篇)—— 预测式弹性扩缩容 Operator 落地实现) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(上篇)—— 时序预测模型构建与离线训练) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 6 —— 基于运维专家知识库的智能故障诊断与排查 Operator 实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 5 —— 基于大语言模型(LLM)的实时日志流智能监测 Operator 实现 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 4 —— 基于 Operator 实现大模型私有化部署与管理 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 3(上篇)—— 面向 AI / 算力调度场景:GPU 竞价实例资源池统一调度管理 Operator 开发 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目 2 —— 面向零售 / 电商潮汐流量难题:多云多集群数据中心级全链路弹性伸缩 DataCenter Scaler Operator 从 0 到 1 全链路开发 Kubernetes编程 / Operator专题【左扬精讲】—— 深入理解Kubebuilder注解:为什么Operator开发离不开这些特殊注释 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目1 —— Applicaion Operator(通用应用生命周期管理 Operator 实战) Pod 镜像拉取失败?kubectl edit pods修改镜像地址的底层原理与实操 (该方法仅为临时应急方案,并非长期解决方案) Kubernetes编程/Operator专题精讲—— 理解控制器模式 —— 控制器模式的核心原理与实现逻辑(从原理到实践) 【AIOPS】AI Agent 专题【左扬精讲】模型微调实战:一站式平台 LLaMA-Factory 【AIOPS】AI Agent 专题【左扬精讲】基于 k8s+vLLM+Ray 分布式部署全指南:架构设计、资源调度与性能优化 【AIOPS】AI Agent专题【左扬精讲】非量化版DeepSeek分布式部署全指南:精度保障、显存规划与Ollama/vLLM选型 【AIOPS】AI Agent 专题【左扬精讲】零开发框架实现 ReAct Agent(Go SRE友好)
VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 模块依赖图——从 import 语句看组件关系
左扬 · 2026-06-29 · via 博客园 - 左扬

VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 模块依赖图——从 import 语句看组件关系

当你打开 VictoriaMetrics 的源码目录时,是否曾被数百个 Go 文件所困惑?每个目录承载什么职责?组件之间如何协作?数据从写入到查询的完整链路是什么?理解模块依赖关系是理解整个系统架构的关键。

读完本篇,你应该能回答:VictoriaMetrics 的源码目录结构是如何组织的?各模块之间的依赖关系是什么?从 import 语句能看出哪些设计模式?为什么某些模块不能相互依赖?

VictoriaMetrics 模块依赖 import 架构设计 Go 工程 目录结构 v1.146.0

学习重点提示建议先通读全文,再重点回顾标注内容

重点掌握(必须)

  • app/ 目录结构:三个主程序入口(app/vminsert/app/vmselect/app/vmstorage/
  • lib/ 目录结构:核心库(lib/storage/lib/mergeset/lib/encoding/
  • 依赖方向:高层依赖低层,应用依赖核心库
  • 循环依赖检测:为什么某些依赖是禁止的

次重点(了解即可)

  • vendor 目录的作用
  • 第三方依赖的管理方式
  • 模块内聚性原则

文章目录

一、问题的起点:理解大型 Go 项目的目录结构

思考记忆提示理解模块依赖是理解架构的第一步——从 import 语句可以看出设计的精髓

  • Go 的 import 语句定义了显式依赖
  • 模块依赖图揭示了设计决策
  • 良好的依赖结构是可维护性的基础
  • 面试高频提问:如何分析一个 Go 项目的架构?

当你接手一个大型 Go 项目时,首先面对的就是目录结构。VictoriaMetrics 有数百个 Go 文件,分布在几十个目录中。如果不理解这些目录的职责划分和依赖关系,阅读源码时会像在迷宫中行走。

我的理解的意思是说

理解模块依赖可以类比为理解一个公司的组织架构

部门 = 模块

一个大公司有很多部门:市场部、研发部、财务部、人力资源部。每个部门有自己的职责(单一职责原则)。

汇报关系 = 依赖关系

汇报关系决定了谁向谁汇报:

  • 小组长 → 部门经理 → 总监 → CTO
  • 这是"向上依赖"的单向关系
  • 不能跨级汇报(如小组长不能直接找 CTO),除非有特殊情况

依赖原则 = 组织原则

公司有很多组织原则:

  • 向上依赖:下级依赖上级(正常)
  • 禁止循环:A 不能依赖 B,B 又依赖 A(混乱)
  • 就近依赖:尽量依赖直接上级,避免跨级依赖

Go 模块同理

  • 向上依赖:app 层依赖 lib 层(正常)
  • 禁止循环:lib/storage 不能依赖 app/vmstorage(循环依赖)
  • 就近依赖:lib/storage 依赖 lib/mergeset,不依赖更远的包

二、顶层目录结构:app/ 和 lib/ 的职责划分

思考记忆提示app/ 和 lib/ 的划分是 VM 最核心的架构决策——理解这个划分就理解了整体架构

  • app/ = 应用层,包含可执行程序的入口
  • lib/ = 核心库,包含可复用的逻辑
  • 这种划分实现了关注点分离
  • 面试高频提问:为什么 VM 采用 app/lib 划分?有什么好处?

VictoriaMetrics 的顶层目录非常简洁,主要分为两大部分:

VictoriaMetrics 顶层目录结构:

victoria-metrics/
│
├── app/                          # 应用层(Application)
│   ├── vminsert/                 # 写入接入服务(可执行程序)
│   ├── vmselect/                 # 查询执行服务(可执行程序)
│   ├── vmstorage/                # 数据存储服务(可执行程序)
│   ├── vmagent/                  # 抓取代理(可执行程序)
│   ├── vmalert/                  # 告警引擎(可执行程序)
│   ├── vmauth/                   # 认证代理(可执行程序)
│   ├── vmbackup/                 # 备份工具(可执行程序)
│   ├── vmrestore/                # 恢复工具(可执行程序)
│   ├── vmctl/                    # 数据迁移工具(可执行程序)
│   ├── vmgateway/                # API 网关(可执行程序)
│   └── vmui/                     # 前端 UI(可执行程序)
│
├── lib/                          # 核心库(Library)
│   ├── storage/                  # 存储引擎核心
│   ├── mergeset/                # MergeSet 存储引擎
│   ├── encoding/                # 压缩编码
│   ├── prompb/                  # Prometheus 协议缓冲区
│   ├── protoparser/             # 协议解析器
│   ├── promql/                  # PromQL 执行引擎
│   ├── promscrape/              # Prometheus 抓取
│   ├── promrelabel/             # Prometheus Relabel
│   ├── fs/                      # 文件系统工具
│   ├── memory/                  # 内存管理
│   └── ...
│
├── vendor/                       # 第三方依赖
├── go.mod                        # Go 模块定义
├── go.sum                        # 依赖校验
└── Makefile                      # 构建脚本

设计原则:
- app/ 包含可执行程序的 main 函数
- lib/ 包含可复用的核心逻辑
- app/ 依赖 lib/,但 lib/ 不能依赖 app/
- lib/ 内部可以相互依赖,但必须保持单向

设计精髓

app/lib 划分的核心价值在于关注点分离

  • 可测试性:lib/ 是纯逻辑,不依赖 main 函数,可以单独测试
  • 可复用性:lib/ 的代码可以被多个 app 复用(如 vmagent 和 vmalert 都用 prompb)
  • 可维护性:修改 app 不影响 lib,修改 lib 不影响 app
  • 清晰架构:依赖方向单一,架构清晰易懂

这种划分是 Go 项目的最佳实践,也是 VictoriaMetrics 能够独立演进各个组件的关键。

三、app/ 目录:三个主程序入口

思考记忆提示app/ 中的三个主程序(vminsert/vmselect/vmstorage)是 Cluster 模式的核心

  • vminsert = 写入接入层
  • vmselect = 查询执行层
  • vmstorage = 数据存储层
  • 面试高频提问:Cluster 模式的三个组件是如何协作的?

3.1 vminsert:写入接入层

vminsert 是 VictoriaMetrics Cluster 模式的写入入口,负责接收来自 Prometheus、vmagent 等客户端的写入请求。

// app/vminsert/main.go(vminsert 主入口)
package main

import (
    // HTTP 服务器和路由
    "net/http"

    // 核心存储接口
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"

    // 网络存储客户端(用于转发请求到 vmstorage)
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/netstorage"

    // 协议解析器(支持多种写入协议)
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser"
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
)

// vminsert 的职责:
// 1. 接收 HTTP 请求(支持 Prometheus remote_write 协议)
// 2. 解析 tenant 信息(从 URL 路径中提取 accountID:projectID)
// 3. 路由请求到对应的 vmstorage 节点
// 4. 聚合多个 vmstorage 的响应

func main() {
    // 注册 HTTP 路由
    http.HandleFunc("/insert/", handleInsert)

    // 启动 HTTP 服务器
    http.ListenAndServe(":8480", nil)
}

func handleInsert(w http.ResponseWriter, r *http.Request) {
    // 1. 解析 tenant
    tenant := parseTenant(r.URL.Path)

    // 2. 路由到 vmstorage
    storageNode := routeToStorage(tenant)

    // 3. 转发请求
    netstorage.Write(r.Body, storageNode)
}

// 依赖关系分析:
// vminsert → lib/storage (接口定义)
// vminsert → lib/netstorage (网络通信)
// vminsert → lib/protoparser (协议解析)
// vminsert → lib/prompb (协议定义)

3.2 vmselect:查询执行层

vmselect 是 VictoriaMetrics Cluster 模式的查询入口,负责接收来自 Grafana、API 客户端的查询请求。

// app/vmselect/main.go(vmselect 主入口)
package main

import (
    // HTTP 服务器
    "net/http"

    // PromQL 执行引擎
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"

    // 网络存储客户端
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/netstorage"

    // 核心存储接口
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
)

// vmselect 的职责:
// 1. 接收 HTTP 查询请求(PromQL 表达式)
// 2. 解析 tenant 信息
// 3. 路由查询到对应的 vmstorage 节点
// 4. 聚合多个 vmstorage 的结果
// 5. 执行 PromQL(部分计算可以在 vmselect 完成)

func main() {
    // 注册 HTTP 路由
    http.HandleFunc("/select/", handleSelect)

    // 启动 HTTP 服务器
    http.ListenAndServe(":8481", nil)
}

func handleSelect(w http.ResponseWriter, r *http.Request) {
    // 1. 解析 tenant
    tenant := parseTenant(r.URL.Path)

    // 2. 解析 PromQL
    query := r.URL.Query().Get("query")
    expr, err := promql.Parse(query)
    if err != nil {
        http.Error(w, err.Error(), 400)
        return
    }

    // 3. 路由到 vmstorage
    storageNodes := getStorageNodes(tenant)

    // 4. 执行查询
    results := netstorage.Select(expr, storageNodes)

    // 5. 聚合结果
    aggregated := promql.Aggregate(results)

    // 6. 返回结果
    writeJSON(w, aggregated)
}

// 依赖关系分析:
// vmselect → lib/promql (PromQL 执行)
// vmselect → lib/netstorage (网络通信)
// vmselect → lib/storage (接口定义)

3.3 vmstorage:数据存储层

vmstorage 是 VictoriaMetrics Cluster 模式的数据存储节点,负责实际的数据读写。

// app/vmstorage/main.go(vmstorage 主入口)
package main

import (
    // 核心存储实现
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"

    // HTTP 服务器
    "net/http"
)

// vmstorage 的职责:
// 1. 管理本地数据存储(Partition、Part、indexDB)
// 2. 处理来自 vminsert 的写入请求
// 3. 处理来自 vmselect 的查询请求
// 4. 维护数据索引(倒排索引)
// 5. 执行后台合并任务

func main() {
    // 初始化存储
    s := storage.New()

    // 注册 HTTP 路由
    http.HandleFunc("/storage/", s.Handle)

    // 启动 HTTP 服务器
    http.ListenAndServe(":8482", nil)
}

// 依赖关系分析:
// vmstorage → lib/storage (存储实现)
// vmstorage 不依赖其他 app 模块

四、lib/ 目录:核心库的层次结构

思考记忆提示lib/ 中的核心库是 VM 最重要的部分——理解它们的层次关系是关键

  • lib/storage = 最底层,存储引擎核心
  • lib/mergeset = 基于 storage 的存储实现
  • lib/encoding = 最底层,压缩编码
  • 面试高频提问:lib/ 中各模块的依赖顺序是什么?

4.1 lib/ 的模块层次

lib/ 目录的模块层次(从底层到顶层):

┌─────────────────────────────────────────────────────────────────────────┐
│                         lib/ 模块层次图                                    │
│                                                                          │
│  第四层:应用层协议                                                      │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  promql/        - PromQL 执行引擎                                │   │
│  │  graphite/      - Graphite API                                   │   │
│  │  prometheus/    - Prometheus API                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                              │                                            │
│                              ▼                                            │
│  第三层:协议处理                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  prompb/        - Prometheus 协议缓冲区                          │   │
│  │  protoparser/   - 协议解析器(支持 12+ 种协议)                  │   │
│  │  promscrape/    - Prometheus 抓取(vmagent 使用)                │   │
│  │  promrelabel/   - Relabel 规则                                   │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                              │                                            │
│                              ▼                                            │
│  第二层:存储实现                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  storage/       - 存储引擎核心(分区、索引、缓存)                │   │
│  │  mergeset/     - MergeSet 存储引擎(数据块管理)                  │   │
│  │  netstorage/   - 网络存储(Cluster 模式通信)                     │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                              │                                            │
│                              ▼                                            │
│  第一层:基础工具                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  encoding/     - 压缩编码(NearestDelta、ZSTD)                   │   │
│  │  bytesutil/    - 字节处理工具(零拷贝)                          │   │
│  │  fs/           - 文件系统工具(mmap、fadvise)                   │   │
│  │  memory/       - 内存管理(Allowed())                           │   │
│  │  fastcache/    - 高性能缓存                                      │   │
│  │  logger/       - 日志                                            │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│  依赖原则:上层依赖下层,下层不能依赖上层                                 │
└─────────────────────────────────────────────────────────────────────────┘

4.2 核心模块详解

lib/storage 是最核心的模块,定义了存储引擎的核心接口和数据结构。

// lib/storage/storage.go(存储引擎核心)
package storage

// Storage 是 VM 存储引擎的核心接口
// 定义了所有存储操作的标准方法
type Storage struct {
    // 分区管理
    tables map[string]*Table

    // 缓存
    tsidCache        *workingsetcache.Cache
    metricIDCache     *workingsetcache.Cache
    metricNameCache   *workingsetcache.Cache

    // 索引数据库
    indexDB *IndexDB

    // 配置
    retentionPeriod uint64
    maxSamplesPerSeries uint64
}

// 添加数据点
func (s *Storage) Add(rows []RawRow) error {
    // 1. 解析 metric name
    // 2. 查找/创建 TSID
    // 3. 写入分区
    // 4. 更新索引
}

// 查询数据
func (s *Storage) Search(q *SearchQuery) (*SearchResult, error) {
    // 1. 解析标签过滤器
    // 2. 查询倒排索引
    // 3. 扫描数据块
    // 4. 归并结果
}

// lib/storage 依赖的模块(按 import 分析):
// lib/storage → lib/encoding (压缩)
// lib/storage → lib/bytesutil (字节处理)
// lib/storage → lib/fs (文件系统)
// lib/storage → lib/memory (内存管理)
// lib/storage → lib/fastcache (缓存)

小贴士模块依赖分析工具

可以使用 Go 的工具分析模块依赖:

  • go mod graph:查看模块依赖图
  • go list -m all:列出所有依赖模块
  • go mod why <module>:解释为什么依赖某个模块

五、模块依赖图:从 import 分析组件关系

思考记忆提示通过 import 语句可以还原完整的模块依赖图

  • 每条 import 语句表示一个显式依赖
  • 依赖关系必须是单向无环
  • 面试高频提问:如何检测 Go 项目中的循环依赖?

5.1 完整依赖图

以下是从 VictoriaMetrics 源码中提取的模块依赖关系:

┌─────────────────────────────────────────────────────────────────────────┐
│                    VictoriaMetrics 模块依赖图                                │
│                                                                          │
│                         ┌──────────────┐                                 │
│                         │    app/       │                                 │
│                         │  (3个主程序)  │                                 │
│                         └───────┬───────┘                                 │
│                                 │                                          │
│          ┌─────────────────────┼─────────────────────┐                   │
│          │                     │                     │                    │
│          ▼                     ▼                     ▼                    │
│   ┌──────────────┐     ┌──────────────┐     ┌──────────────┐            │
│   │   vminsert   │     │   vmselect  │     │  vmstorage   │            │
│   └───────┬──────┘     └───────┬──────┘     └──────┬───────┘            │
│           │                     │                     │                    │
│           └─────────────┬───────┘                     │                    │
│                         ▼                             │                    │
│                  ┌──────────────┐                     │                    │
│                  │  netstorage  │                     │                    │
│                  └───────┬──────┘                     │                    │
│                          │                             │                    │
│                          └─────────┬───────────────────┘                    │
│                                    ▼                                        │
│                           ┌──────────────┐                                  │
│                           │   storage    │                                  │
│                           └───────┬──────┘                                  │
│                                   │                                         │
│           ┌───────────────────────┼───────────────────────┐                │
│           │                       │                       │                │
│           ▼                       ▼                       ▼                │
│    ┌──────────────┐         ┌──────────────┐       ┌──────────────┐     │
│    │   mergeset  │         │   indexDB    │       │   cache      │     │
│    └───────┬──────┘         └──────────────┘       └──────────────┘     │
│            │                                                              │
│            └──────────────────┬───────────────────────────────────┐       │
│                               ▼                                       │       │
│                        ┌──────────────┐                             │       │
│                        │   encoding   │                             │       │
│                        └──────────────┘                             │       │
│                                                                       │       │
│  ┌──────────────────────────────────────────────────────────────────┐ │       │
│  │                       lib/ 基础工具层                           │ │       │
│  │  bytesutil │ fs │ memory │ logger │ fastcache │ timerpool   │ │       │
│  └──────────────────────────────────────────────────────────────────┘ │       │
│                                                                       │       │
└───────────────────────────────────────────────────────────────────────┘       │
│                                                                          │
│  图例:                                                                  │
│    A → B 表示 A 依赖 B                                                   │
│    数据流向:app → storage → mergeset → encoding                        │
│    依赖方向:相反                                                         │
└─────────────────────────────────────────────────────────────────────────┘

5.2 依赖关系详解

从 import 语句可以分析出以下关键依赖关系:

模块依赖说明
app/vminsert storage, netstorage, protoparser 写入入口,依赖存储接口
app/vmselect storage, netstorage, promql 查询入口,依赖 PromQL 引擎
app/vmstorage storage 存储节点,核心依赖
lib/storage mergeset, encoding, bytesutil, fs, memory 存储核心,依赖底层库
lib/mergeset encoding, bytesutil, fs 存储实现,依赖编码工具
lib/promql storage, netstorage 查询引擎,依赖存储接口
lib/encoding bytesutil 最底层之一,只依赖字节工具

注意

依赖关系必须是有向无环图(DAG)。如果出现循环依赖(如 A → B → C → A),Go 编译器会报错。可以使用 go mod why -m <module> 检测循环依赖。

六、依赖原则:为什么这样组织

思考记忆提示理解依赖原则才能理解架构决策——这些原则不是凭空制定的

  • 依赖倒置原则:高层定义接口,低层实现接口
  • 稳定依赖原则:不稳定的模块不应该被稳定的模块依赖
  • 循环依赖检测:Go 编译器会自动检测循环依赖
  • 面试高频提问:如何设计一个好的模块依赖结构?

6.1 依赖原则详解

VictoriaMetrics 的模块组织遵循以下原则:

┌─────────────────────────────────────────────────────────────────────────┐
│                    模块依赖设计原则                                        │
│                                                                          │
│  1. 依赖方向:上层依赖下层                                                │
│     ┌────────┐                                                         │
│     │  app/  │ ← 应用层(依赖 lib/)                                     │
│     └────┬───┘                                                         │
│          │                                                              │
│          ▼                                                              │
│     ┌────────┐                                                         │
│     │ lib/   │ ← 核心库(被 app/ 依赖)                                 │
│     └────────┘                                                         │
│                                                                          │
│  2. 禁止循环依赖                                                         │
│     ┌────────────────────────────────────────┐                         │
│     │  A → B → C → A  ❌ 编译错误              │                         │
│     │  A → B → C → B  ❌ 编译错误              │                         │
│     └────────────────────────────────────────┘                         │
│                                                                          │
│  3. 就近依赖原则                                                         │
│     ┌────────────────────────────────────────┐                         │
│     │  A → B → C → D                          │                         │
│     │  ✓ A 直接依赖 D(如果需要 D 的功能)      │                         │
│     │  ✗ A 绕过 B, C 直接依赖 D(可能破坏封装) │                         │
│     └────────────────────────────────────────┘                         │
│                                                                          │
│  4. 接口隔离原则                                                         │
│     ┌────────────────────────────────────────┐                         │
│     │  存储接口 vs 存储实现                    │                         │
│     │  lib/storage 定义接口                   │                         │
│     │  app/vmstorage 实现接口                 │                         │
│     │  这样可以替换存储实现而不影响上层         │                         │
│     └────────────────────────────────────────┘                         │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

设计精髓

良好的模块依赖结构带来以下好处:

  • 可测试性:可以用 mock 替换实现,单独测试每一层
  • 可维护性:修改某一层不会影响其他层
  • 可复用性:底层模块可以被多个上层模块复用
  • 可理解性:依赖图清晰,架构一目了然
  • 可演进性:可以独立演进各层

VM 的架构是 Go 项目中"干净架构"的典范,很值得学习和借鉴。

6.2 实际依赖示例

让我们通过实际的 import 语句来验证依赖关系:

// app/vminsert/main.go 的 import
import (
    // 标准库
    "net/http"
    "fmt"

    // lib/ 核心库
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"     // 存储接口
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/netstorage" // 网络存储
    "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"    // Prometheus 协议

    // 第三方库
    "github.com/golang/glog"  // 日志
)

// 依赖方向:
// vminsert → storage (定义)
// vminsert → netstorage (实现)
// vminsert → prompb (定义)
// 注意:vminsert 不依赖 app/vmselect 或 app/vmstorage
// 这是关键:app 层内部不能相互依赖

七、FAQ:常见疑问

思考记忆提示FAQ 是全篇的"临考前速背"模块,20 组覆盖全链路

  • Q1-Q5 围绕目录结构:app/ 和 lib/ 的职责划分
  • Q6-Q10 围绕依赖关系:为什么这样组织
  • Q11-Q15 围绕工具使用:如何分析依赖
  • Q16-Q20 围绕设计原则:架构设计决策

Q1. app/ 和 lib/ 的本质区别是什么?

app/ 包含 main 函数和可执行程序的入口,lib/ 包含可复用的逻辑库。app/ 中的每个子目录是一个可执行程序(如 vminsert、vmselect、vmstorage),每个都有自己的 main.go。lib/ 中的每个子目录是一个 Go 包(如 storage、mergeset、encoding),提供特定功能。

Q2. 为什么 lib/ 不能依赖 app/?

为了避免循环依赖和保持关注点分离。lib/ 是底层库,应该独立于应用。如果 lib/ 依赖 app/,那么修改 app/ 就可能影响 lib/,破坏了模块的独立性。

Q3. lib/ 内部可以相互依赖吗?

可以,但必须是单向无环的。lib/ 内部的依赖规则是:下层可以依赖更下层,但上层不能依赖上层。例如,storage 可以依赖 mergeset,mergeset 可以依赖 encoding,但 encoding 不能依赖 storage(会形成循环)。

Q4. 什么是依赖倒置原则?

高层定义接口,低层实现接口,依赖方向从低层指向高层。例如,vminsert 定义了"存储接口",vmstorage 实现了这个接口。这样 vminsert 不需要知道具体是哪个 vmstorage 实例在处理数据。

Q5. Go 如何检测循环依赖?

Go 编译器会在构建时自动检测循环依赖,如果发现会报错。错误消息类似于 "import cycle not allowed"。可以通过 go mod why -m <module> 来追踪依赖路径。

Q6. 如何查看一个模块的依赖关系?

使用 go mod graph 查看完整依赖图,或使用 IDE 的依赖分析功能。IDE(如 GoLand、VSCode)通常有"Show Dependencies"功能,可以可视化模块依赖图。

Q7. vendor/ 目录的作用是什么?

vendor/ 存储所有第三方依赖的副本,用于离线构建和版本锁定。Go 1.6+ 会优先使用 vendor/ 中的依赖,而不是从网络下载。这确保了构建的可重复性。

Q8. 为什么有些模块在 app/ 中,有些在 lib/ 中?

有 main 函数的就是 app/,没有 main 函数的就是 lib/。这是最简单也最有效的划分原则。vmagent、vmalert、vmauth 等是独立工具,自然放在 app/ 中。

Q9. 模块依赖图对于阅读源码有什么帮助?

帮助定位问题和理解数据流。当你遇到一个问题时,可以沿着依赖方向向上追踪,找到问题的根源。当你需要修改某个模块时,可以沿着依赖方向向下检查,影响的范围。

Q10. VictoriaMetrics 的目录结构有什么值得学习的地方?

app/lib 划分清晰、依赖方向单一、模块职责明确。这种结构是 Go 项目的最佳实践,适合大型项目的组织和维护。

Q11. 什么是包的内部可见性?

Go 中小写字母开头的标识符是包内私有的,只能在同一个包内访问。这实现了封装性,外部只能通过导出的(首字母大写)标识符访问包的内部实现。

Q12. 如何避免循环依赖?

提取公共接口到独立包,让依赖双方都依赖这个接口包。例如,A 和 B 相互依赖,可以创建一个 ABlib 包,让 A 和 B 都依赖 ABlib。

Q13. 什么是 Go modules?

Go modules 是 Go 1.11+ 引入的依赖管理机制,通过 go.mod 和 go.sum 文件管理依赖版本。go.mod 声明模块名和依赖,go.sum 记录依赖的校验和。

Q14. 什么是 go.mod 的 module 路径?

module 路径是模块的唯一标识符,用于定位模块和解析 import 路径。VictoriaMetrics 的 module 路径是 github.com/VictoriaMetrics/VictoriaMetrics。

Q15. 如何确定一个模块的版本?

通过 go.mod 中的版本号,如 v1.146.0。Go modules 使用语义化版本(semver),版本号格式为 vMAJOR.MINOR.PATCH。

Q16. 什么是语义化版本?

语义化版本(semver)是一种版本命名规范,格式为 MAJOR.MINOR.PATCH。MAJOR 是不兼容的修改,MINOR 是向后兼容的功能增加,PATCH 是向后兼容的问题修复。

Q17. 什么是 module proxy?

module proxy 是一个 HTTP 服务器,代理 Go 模块的下载和缓存。可以使用私有 proxy 来加速下载和提高构建的可重复性。

Q18. 什么是 replace directive?

replace 指令用于在本地开发时替换依赖的模块路径。例如,replace github.com/foo/bar => ../bar 可以让 go build 使用本地的 bar 模块而不是远程的。

Q19. 什么是 go.sum?

go.sum 记录了每个依赖包的加密校验和,用于验证下载的包是否被篡改。每次添加新依赖时,go mod tidy 会自动更新 go.sum。

Q20. 如何清理不需要的依赖?

使用 go mod tidy 命令,它会自动添加缺失的依赖和删除未使用的依赖。

全篇必记总纲

VictoriaMetrics 的模块依赖结构遵循app/lib 划分 + 单向无环依赖原则:app/ 包含可执行程序的入口,lib/ 包含可复用的核心库;高层(app)依赖低层(lib),低层不能依赖高层;lib 内部依赖方向也是单向的(encoding → mergeset → storage)。这种结构是 Go 项目"干净架构"的典范。

八、Roadmap:后续预告

本篇覆盖了 VictoriaMetrics 的模块依赖图,但还有很多细节尚未展开:

  • #09 性能模型:写入吞吐/查询延迟/内存占用的数学模型——理解 VM 的性能上限
  • #10 与其他 TSDB 对比:Prometheus/InfluxDB/Thanos/VM——理解 VM 在竞品中的定位
  • #02 全局架构:Single-Node vs Cluster 模式——理解两种部署方式
  • #12 源码阅读路线图:如何高效阅读 VM 源码——最佳实践
  • #161 完整写入链路:一个数据点从 HTTP 到 Part 文件——源码追踪

本文参考与源码链接:
  • app/ · 应用层入口
  • lib/ · 核心库
  • go.mod · 模块定义
  • Go Modules 官方文档