# 第 8 部分导论 · Grafana 与数据源抽象

> Grafana 自己不存任何数据，它凭什么能一统可视化？答案是数据源抽象。这一章读 pkg/plugins 的插件接口，讲清 QueryData 这个统一契约、后端插件为什么是独立进程，以及 Grafana 在链条里的真正位置。

- 作者：David（道雾轩）
- 专栏：指标采集与观测（https://daiw.org/manual/observability.md）
- 最后更新：2026-07-15
- 原文：https://daiw.org/manual/observability/grafana-datasource
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 第 8 部分导论 · Grafana 与数据源抽象

数据[采集了](https://daiw.org/manual/observability/scrape-loop)、[存好了](https://daiw.org/manual/observability/tsdb-overview)、[能查了](https://daiw.org/manual/observability/promql-model)、[能告警了](https://daiw.org/manual/observability/slo-burn-rate)、[三支柱也串起来了](https://daiw.org/manual/observability/exemplars)。最后一个问题：**怎么把它变成人能看懂的东西？**

这是 **Grafana** 的活。而它有个反常识的起点。

## 它自己不存数据

<Callout type="info">
  **Grafana **不是一个数据库**。它不存任何时序数据、日志、trace。它是一个**前端 + 查询代理**。**

  ```
  你在 Grafana 上画的每一条曲线:
    Grafana 前端 → Grafana 后端 → [Prometheus](https://daiw.org/manual/observability/promql-model) → 返回数据 → 画图
                                 ↑ 它只是转发和渲染
  ```

  **这个“不存数据”的定位，恰恰是它能一统可视化江湖的原因：**

  - **它不和任何后端竞争** —— 它不想取代 Prometheus/ES/Loki，**它想成为它们所有人的前端**。于是每个后端都乐意为它写数据源插件。
  - **它可以支持一切** —— 因为不存数据，它对“数据长什么样”没有偏见。SQL 数据库、时序库、日志库、云厂商 API、甚至一个 CSV 文件，**都能被抽象成“数据源”**。
  - **换后端不用换看板** —— 一定程度上。

  **对比[第 0 部分讲的三个主角](https://daiw.org/manual/observability/the-three-projects)**：[Prometheus 是“存储引擎”问题](https://daiw.org/manual/observability/tsdb-overview)、[Collector 是“数据管道”问题](https://daiw.org/manual/observability/collector-architecture)、**Grafana 是“统一抽象”问题**——它要把千差万别的后端，统一成一个人能理解的心智模型。**这是三类完全不同性质的工程难题。**
</Callout>

## 核心契约：QueryData

看后端插件接口（真实源码 `pkg/plugins/backendplugin/ifaces.go`）：

```go
// Plugin is the backend plugin interface.
type Plugin interface {
    PluginID() string
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    // ...
    backend.QueryDataHandler       // ← 核心:查数据
    backend.CheckHealthHandler     // ← 健康检查(界面上那个"Save & Test")
    backend.CallResourceHandler    // ← 自定义 HTTP 端点(如 PromQL 自动补全)
    backend.StreamHandler          // ← 流式数据(实时推送)
    backend.CollectMetricsHandler  // ← 插件自己的指标
}
```

<Callout type="info">
  **`QueryDataHandler` 是整个 Grafana 的**窄腰**：**

  ```
  面板问:"给我这个时间范围的数据"
       ↓ QueryData(请求里带:查询语句、时间范围、最大点数、间隔)
  插件答:"给你一组 DataFrame"
  ```

  **所有数据源——Prometheus、MySQL、Loki、CloudWatch、Elasticsearch——都只需实现这一个方法。** 它们内部千差万别（[PromQL](https://daiw.org/manual/observability/promql-model) vs SQL vs LogQL vs 某云的 REST API），但**对上只暴露 `QueryData`**。

  **返回的 `DataFrame` 是另一半关键**：它是个**列式的表格结构**（类似 Apache Arrow 的思路）——**不管你查的是时序、日志还是表格，都被表达成 DataFrame**。于是 Grafana 的面板只需要认识 DataFrame，不用认识 20 种后端格式。

  **这是“[接口即边界](https://daiw.org/manual/observability/reading-the-source)”的又一个范例**，和 [Prometheus 的 `storage.Querier`](https://daiw.org/manual/observability/query-path)、[Collector 的 `consumer`](https://daiw.org/manual/observability/consumer-backpressure) 完全同构：**一个小接口 + 一个统一的数据表示 = 生态可以无限长**。

  **这三个项目，在三个层面，得出了同一个结论。**
</Callout>

## 后端插件是独立进程

一个容易忽略但很重要的设计：

<Callout type="info">
  **Grafana 的后端数据源插件**不是动态库，而是**独立进程**——通过 gRPC 和 Grafana 主进程通信（用的是 HashiCorp 的 go-plugin 框架）。**

  **为什么这么麻烦？**

  - **隔离崩溃** —— 一个写得烂的第三方插件**崩了，不会带走整个 Grafana**。如果是动态库，一个 panic 就全完了。
  - **隔离依赖** —— 插件可以用任何 Go 版本、任何依赖，**不用和 Grafana 主程序的依赖打架**。
  - **可以用别的语言写** —— 理论上任何能说 gRPC 的语言。
  - **能限制资源** —— 插件进程可以被单独限制 CPU/内存。

  **代价是**：进程间通信有开销、部署更复杂、调试更麻烦。

  **`Target` 那个枚举**（`TargetNone`/`TargetInMemory`/`TargetLocal`）透露了另一件事：**有些插件其实跑在进程内**（`in_memory`）——**核心自带的数据源为了性能不走 gRPC**。这是个务实的妥协：**第三方插件要隔离，自家的可以信任。**
</Callout>

## Grafana 在链条里的位置

```mermaid
flowchart LR
    subgraph 存储["各种后端(存数据的)"]
        P["Prometheus/Mimir<br/>(指标)"]
        L["Loki<br/>(日志)"]
        T["Tempo/Jaeger<br/>(追踪)"]
        S["MySQL/PG<br/>(业务数据)"]
    end
    P & L & T & S -->|"QueryData<br/>(各自的插件)"| G["Grafana<br/>(不存数据!)"]
    G --> D["看板 / Explore / 告警"]
    style G fill:#a855f7,color:#fff
```

<Callout type="info">
  **注意“告警”也在 Grafana 里** —— 这是近年的重要变化（**unified alerting**，源码 `pkg/services/ngalert/`，README 里叫它 “Next generation alerting”）。

  **Grafana 的告警和 [Prometheus 的告警](https://daiw.org/manual/observability/alerting-rules)是两套并存的东西**，容易混淆：

  | | **Prometheus 告警** | **Grafana 告警** |
  | --- | --- | --- |
  | **在哪求值** | Prometheus 自己（[`rules/`](https://daiw.org/manual/observability/alerting-rules)） | Grafana 后端 |
  | **能跨数据源吗** | ❌ 只能查自己的数据 | **✅ 能**（它能查任何数据源） |
  | **依赖** | [无外部依赖，单机自足](https://daiw.org/manual/observability/prometheus-architecture) | 依赖 Grafana 活着 |
  | **配置** | YAML 规则文件 | UI 或 provisioning |

  **怎么选？一个务实的判据：**

  - **核心的、[必须最可靠的告警](https://daiw.org/manual/observability/alerting-philosophy)** → **放 Prometheus**。理由就是[第 1 部分那条](https://daiw.org/manual/observability/prometheus-architecture)：**监控必须比被监控更可靠**，而 Prometheus [无外部依赖](https://daiw.org/manual/observability/prometheus-architecture)。**如果 Grafana 挂了，你的 SLO 告警不该跟着瞎。**
  - **需要跨数据源的、或者业务方要自助配的** → **放 Grafana**。比如“指标异常 **且** 数据库里的订单表也异常”这种，**只有 Grafana 能做**。

  **别把所有告警都堆进 Grafana**——那等于给你的告警链路加了一个新的单点。
</Callout>

## 这一部分怎么安排

Grafana 有 **1.5 万个 Go/TS 文件**，[绝大部分是 UI 和业务](https://daiw.org/manual/observability/reading-the-source)。本部分**只挑三个真正值得学的**：

- **数据源抽象**（本章）—— `QueryData` 那个窄腰。
- [看板与查询](https://daiw.org/manual/observability/grafana-dashboards) —— 变量、模板、以及“看板即代码”。
- [Explore 与关联](https://daiw.org/manual/observability/grafana-explore) —— [三支柱的桥](https://daiw.org/manual/observability/exemplars)在 UI 上怎么落地，收尾第 8 部分。

## 小结与下一章

**Grafana 自己不存数据**——它是**前端 + 查询代理**，而这个定位正是它能一统可视化的原因：[不和任何后端竞争（所以人人乐意写插件）、对数据形态无偏见（什么都能抽象成“数据源”）](https://daiw.org/manual/observability/the-three-projects)。核心契约是 **`QueryDataHandler`**（[`pkg/plugins/backendplugin/ifaces.go`](https://daiw.org/manual/observability/reading-the-source)）：[所有数据源不管内部是 PromQL/SQL/LogQL/REST，对上只暴露这一个方法，返回统一的列式 **DataFrame**](https://daiw.org/manual/observability/promql-model)——**一个小接口 + 一个统一数据表示 = 生态无限长**；这和 [`storage.Querier`](https://daiw.org/manual/observability/query-path)、[`consumer`](https://daiw.org/manual/observability/consumer-backpressure) 完全同构，**三个项目在三个层面得出同一结论**。**后端插件是独立进程**（gRPC）：[隔离崩溃、隔离依赖、可换语言、可限资源](https://daiw.org/manual/observability/collector-architecture)；但 `Target` 枚举透露[自家插件跑进程内以求性能](https://daiw.org/manual/observability/grafana-datasource)——**第三方要隔离，自家可信任**。**Grafana 也有告警**（`ngalert`），和 [Prometheus 告警](https://daiw.org/manual/observability/alerting-rules)**并存**：[核心告警放 Prometheus（无外部依赖，Grafana 挂了 SLO 告警不该瞎）、跨数据源的放 Grafana](https://daiw.org/manual/observability/prometheus-architecture)——**别把所有告警堆进 Grafana，那是给告警链路加单点**。

看板怎么做才有用？下一章。👉 [看板与查询](https://daiw.org/manual/observability/grafana-dashboards)。

## 参考资料

- [1] Grafana 源码 `pkg/plugins/backendplugin/ifaces.go`（`Plugin` 接口与各 handler）、`pkg/services/datasources/`。
- [2] Grafana 源码 `pkg/services/ngalert/README.md`（unified alerting）。
- [3] Grafana 官方文档：Backend plugins、Data frames；关联[三个主角](https://daiw.org/manual/observability/the-three-projects)。
