数据帧
Grafana 支持各种不同的数据源,每个数据源都有自己的数据模型。为了实现这一点,Grafana 将每个数据源的查询结果整合到一个称为*数据帧*的统一数据结构中。
数据帧结构借鉴了 R 编程语言和 Pandas 等数据分析工具的概念。
数据帧在 Grafana 7.0 及更高版本中可用,并取代了时间序列和表格结构,成为可以支持更广泛数据类型的更通用数据结构。
本文档概述了数据帧结构以及 Grafana 内部数据的处理方式。
数据帧字段
数据帧是*字段*的集合,其中每个字段对应一个列。每个字段又由值和元数据(例如这些值的数据类型)的集合组成。
export interface Field<T = any, V = Vector<T>> {
/**
* Name of the field (column)
*/
name: string;
/**
* Field value type (string, number, and so on)
*/
type: FieldType;
/**
* Meta info about how field and how to display it
*/
config: FieldConfig;
/**
* The raw field values
* In Grafana 10, this accepts both simple arrays and the Vector interface
* In Grafana 11, the Vector interface has been removed
*/
values: V | T[];
/**
* When type === FieldType.Time, this can optionally store
* the nanosecond-precison fractions as integers between
* 0 and 999999.
*/
nanos?: number[];
labels?: Labels;
/**
* Cached values with appropriate display and id values
*/
state?: FieldState | null;
/**
* Convert a value for display
*/
display?: DisplayProcessor;
/**
* Get value data links with variables interpolated
*/
getLinks?: (config: ValueLinkConfig) => Array<LinkModel<Field>>;
}
让我们来看一个例子。下表展示了一个包含两个字段(*时间*和*温度*)的数据帧
时间 | 温度 |
---|---|
2020-01-02 03:04:00 | 45.0 |
2020-01-02 03:05:00 | 47.0 |
2020-01-02 03:06:00 | 48.0 |
每个字段有三个值,且字段中的每个值必须具有相同的类型。在本例中,`time` 字段中的所有值都是时间戳,而 `temperature` 字段中的所有值都是数字。
虽然时间字段表示时间戳,但值的类型应为 `Number` (TypeScript) 或 `time.Time` (Golang)。
数据帧中时间字段的另一个限制涉及数字转换。在插件前端代码中,可以使用 `@grafana/data` 包中的 ensureTimeField
函数将其他格式转换为 `Number`。此函数将遵循 ISO 8601 格式的字符串(例如,`2017-07-19 00:00:00.000`)、Javascript `DateTime` 对象以及带有相对时间的字符串(例如,`now-10s`)转换为 `Number`。
数据帧的一个限制是,帧中的所有字段必须具有相同的长度才能成为有效的数据帧。
字段配置
数据帧中的每个字段都包含有关该字段中值的可选信息,例如单位、缩放等。
通过向数据帧添加字段配置,Grafana 可以自动配置可视化。例如,您可以配置 Grafana 自动设置数据源提供的单位。
数据转换
我们已经了解了字段配置如何包含类型信息;此外,数据帧字段还支持 Grafana 内部的*数据转换*。
数据转换是任何接受数据帧作为输入并返回另一个数据帧作为输出的函数。通过在插件中使用数据帧,您可以免费获得一系列转换。
要详细了解 Grafana 中的数据转换,请参阅转换数据。
作为时间序列的数据帧
包含至少一个时间字段的数据帧被视为*时间序列*。
有关时间序列的更多信息,请参阅我们的时间序列简介。
宽格式
当一组时间序列共享相同的*时间索引*(即每个时间序列中的时间字段相同)时,可以将它们存储在一起,采用*宽*格式。通过重用时间字段,发送到浏览器的数据量会减少。
在本例中,来自每个主机的 `cpu` 使用率共享时间索引,因此我们可以将它们存储在同一个数据帧中
Name: Wide
Dimensions: 3 fields by 2 rows
+---------------------+-----------------+-----------------+
| Name: time | Name: cpu | Name: cpu |
| Labels: | Labels: host=a | Labels: host=b |
| Type: []time.Time | Type: []float64 | Type: []float64 |
+---------------------+-----------------+-----------------+
| 2020-01-02 03:04:00 | 3 | 4 |
| 2020-01-02 03:05:00 | 6 | 7 |
+---------------------+-----------------+-----------------+
但是,如果两个时间序列不共享相同的时间值,则它们会表示为两个不同的数据帧
Name: cpu
Dimensions: 2 fields by 2 rows
+---------------------+-----------------+
| Name: time | Name: cpu |
| Labels: | Labels: host=a |
| Type: []time.Time | Type: []float64 |
+---------------------+-----------------+
| 2020-01-02 03:04:00 | 3 |
| 2020-01-02 03:05:00 | 6 |
+---------------------+-----------------+
Name: cpu
Dimensions: 2 fields by 2 rows
+---------------------+-----------------+
| Name: time | Name: cpu |
| Labels: | Labels: host=b |
| Type: []time.Time | Type: []float64 |
+---------------------+-----------------+
| 2020-01-02 03:04:01 | 4 |
| 2020-01-02 03:05:01 | 7 |
+---------------------+-----------------+
宽格式的典型用例是当多个时间序列由同一个进程收集时。在这种情况下,每个测量都在同一时间间隔进行,因此共享相同的时间值。
长格式
某些数据源以*长*格式(也称为*窄*格式)返回数据。例如,SQL 数据库通常返回此格式。
在长格式中,字符串值表示为单独的字段而不是标签。因此,长格式的数据可能会有重复的时间值。
使用适用于 Go 的 Grafana 插件 SDK,插件可以检测并将在长格式的数据帧转换为宽格式。
有关检测和转换数据帧,请参阅此示例
tsSchema := frame.TimeSeriesSchema()
if tsSchema.Type == data.TimeSeriesTypeLong {
wideFrame, err := data.LongToWide(frame, nil)
if err == nil {
// handle error
}
// return wideFrame
}
这里还有一个附加示例。以下数据帧显示为长格式
Name: Long
Dimensions: 4 fields by 4 rows
+---------------------+-----------------+-----------------+----------------+
| Name: time | Name: aMetric | Name: bMetric | Name: host |
| Labels: | Labels: | Labels: | Labels: |
| Type: []time.Time | Type: []float64 | Type: []float64 | Type: []string |
+---------------------+-----------------+-----------------+----------------+
| 2020-01-02 03:04:00 | 2 | 10 | foo |
| 2020-01-02 03:04:00 | 5 | 15 | bar |
| 2020-01-02 03:05:00 | 3 | 11 | foo |
| 2020-01-02 03:05:00 | 6 | 16 | bar |
+---------------------+-----------------+-----------------+----------------+
上表可以转换为如下所示的宽格式数据帧
Name: Wide
Dimensions: 5 fields by 2 rows
+---------------------+------------------+------------------+------------------+------------------+
| Name: time | Name: aMetric | Name: bMetric | Name: aMetric | Name: bMetric |
| Labels: | Labels: host=foo | Labels: host=foo | Labels: host=bar | Labels: host=bar |
| Type: []time.Time | Type: []float64 | Type: []float64 | Type: []float64 | Type: []float64 |
+---------------------+------------------+------------------+------------------+------------------+
| 2020-01-02 03:04:00 | 2 | 10 | 5 | 15 |
| 2020-01-02 03:05:00 | 3 | 11 | 6 | 16 |
+---------------------+------------------+------------------+------------------+------------------+
并非所有面板都支持宽时间序列数据帧格式。为了保持完全向后兼容,Grafana 引入了一种转换,您可以使用它将宽格式转换为长格式。有关用法信息,请参阅准备时间序列转换。
技术参考
Grafana 中数据帧的概念借鉴了 R 编程语言和 Pandas 等数据分析工具。下面提供了其他技术参考。
Apache Arrow
数据帧结构受到 Apache Arrow 项目的启发并使用了它。Javascript 数据帧使用 Arrow Tables 作为底层结构,后端 Go 代码将其 Frames 序列化为 Arrow Tables 以进行传输。
Javascript
数据帧的 Javascript 实现位于 @grafana/data
包的 /src/dataframe
文件夹和 /src/types/dataframe.ts
中。
Go
有关数据帧的 Go 实现文档,请参阅github.com/grafana/grafana-plugin-sdk-go/data 包。
了解更多
有关使用数据帧进行插件开发的指南,请参阅以下主题