Prometheus TSDB文件格式-index

Prometheus TSDB文件格式-index

存储目录

如下所示为Prometheus/TSDB存储目录结构:

drwxrwxr-x 3 service service 4096 Nov  9 12:08 01EPNJJA12FXV9YDYM3MBNE3HP
drwxrwxr-x 3 service service 4096 Nov 10 10:10 01EPQY3Z6AJ700B9DVFWSG0FSD
drwxrwxr-x 3 service service 4096 Nov 11 05:26 01EPSZZJ29EQZ9EQ1Z15EGGRJM
drwxrwxr-x 3 service service 4096 Nov 11 15:30 01EPV3C56BA53YZ4H28PQHBWQV
drwxrwxr-x 3 service service 4096 Nov 11 16:30 01EPV6T1RWCFQ6T4RVGAN2G7BG
drwxrwxr-x 3 service service 4096 Nov 11 17:11 01EPV8V50SMAJ617XQKWZ344HD
drwxrwxr-x 3 service service 4096 Nov 11 17:30 01EPVA7WJ5DXTV6FR06VJ0CT40
-rw------- 1 service service    7 Dec 13  2019 lock
-rw-rw-r-- 1 service service  200 Sep 26  2019 schema
drwxrwxr-x 2 service service 4096 Nov 11 17:42 wal

其中类似'01EPVA7WJ5DXTV6FR06VJ0CT40'的目录为其中一个block。如下所示为block的目录结构:

drwxrwxr-x 2 service service      4096 Nov 11 17:30 chunks
-rw-rw-r-- 1 service service 104684896 Nov 11 17:30 index
-rw-rw-r-- 1 service service       302 Nov 11 17:30 meta.json
-rw-rw-r-- 1 service service         9 Nov 11 17:30 tombstones

其中meta.json文件描述概要信息,一看就懂,不必多言:

{
	"ulid": "01EPVA7WJ5DXTV6FR06VJ0CT40",
	"minTime": 1605081600,
	"maxTime": 1605085200,
	"stats": {
		"numSamples": 1359295562,
		"numSeries": 441979,
		"numChunks": 11207472
	},
	"compaction": {
		"level": 1,
		"sources": [
			"01EPVA7WJ5DXTV6FR06VJ0CT40"
		]
	},
	"version": 2,
	"numChunkFile": 3
}

其中index文件用于定位和查询指标数据,它是本文的主角。

index磁盘格式

首先,声明一下index磁盘格式不等于index到内存里的格式,当然也差不太多。
如下所示为每个block目录下面必有的index文件磁盘格式,index以TOC(table of contents)目录表作为结尾。
TOC也作为index文件的入口:总是从文件尾部倒数N个字节读取TOC开始干活。

┌────────────────────────────┬─────────────────────┐
│ magic(0xBAAAD700) <4b>     │ version(1) <1 byte> │
├────────────────────────────┴─────────────────────┤
│ ┌──────────────────────────────────────────────┐ │
│ │                 Symbol Table                 │ │
│ ├──────────────────────────────────────────────┤ │
│ │                    Series                    │ │
│ ├──────────────────────────────────────────────┤ │
│ │                 Label Index 1                │ │
│ ├──────────────────────────────────────────────┤ │
│ │                      ...                     │ │
│ ├──────────────────────────────────────────────┤ │
│ │                 Label Index N                │ │
│ ├──────────────────────────────────────────────┤ │
│ │                   Postings 1                 │ │
│ ├──────────────────────────────────────────────┤ │
│ │                      ...                     │ │
│ ├──────────────────────────────────────────────┤ │
│ │                   Postings N                 │ │
│ ├──────────────────────────────────────────────┤ │
│ │               Label Offset Table             │ │
│ ├──────────────────────────────────────────────┤ │
│ │             Postings Offset Table            │ │
│ ├──────────────────────────────────────────────┤ │
│ │                      TOC                     │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

上述主要部分(section)之间,在写入index时会追加一些padding字节用于对齐,在读取时,需要跳过那些不在len里的值为0的padding字节。
大部分section都以len字段开始,len指定了该section有效的字节数,在这些有效数据之后,追加了一个checksum,它基于len的有效数据进行CRC32计算。

Symbol Table 字符串符号表

Symbol Table保存了所有sereis的label用到的字符串,并做了排序和去重。在后续的section中,将通过引用字符串的序号,达到降低index空间的目的。
Symbol Table包含一个序列的字符串条目,每个条目包括一个字符串原始字节的长度(len)和字符串实际字节两个部分。字符串的格式是UTF-8,并且按字典序升序排序,通过条目序列的索引号对其进行引用。

┌────────────────────┬─────────────────────┐
│ len <4b>           │ #symbols <4b>       │
├────────────────────┴─────────────────────┤
│ ┌──────────────────────┬───────────────┐ │
│ │ len(str_1) <uvarint> │ str_1 <bytes> │ │
│ ├──────────────────────┴───────────────┤ │
│ │                . . .                 │ │
│ ├──────────────────────┬───────────────┤ │
│ │ len(str_n) <uvarint> │ str_n <bytes> │ │
│ └──────────────────────┴───────────────┘ │
├──────────────────────────────────────────┤
│ CRC32 <4b>                               │
└──────────────────────────────────────────┘

Series 时序数据索引表

该section包含了所有的series时序数据,包括sereis的label sets和其block内的chunks信息,series按label sets的字典序排序。
每个series按16字节对齐,series的ID=offset/16,后面section通过ID对该series进行引用。因此series的ID列表和sereis按字典序排序后的列表一一对应。

┌───────────────────────────────────────┐
│ ┌───────────────────────────────────┐ │
│ │   series_1                        │ │
│ ├───────────────────────────────────┤ │
│ │                 . . .             │ │
│ ├───────────────────────────────────┤ │
│ │   series_n                        │ │
│ └───────────────────────────────────┘ │
└───────────────────────────────────────┘

每个sereis section以len开始,以CRC32结尾。数据区首先是一个8字节数值标识labels的个数,然后是所有的labels,每个label包括name和value的ID号(对Symbol Table的引用)。该series的labels对按字典序排序。
紧随label表格之后是对chunk的索引表,首先是一个8字节数值标识chunk的个数,然后是所有的chunks,每个chunk条目包括:chunk的最小时间mint和最大时间maxt,以及一个8字节数值指向chunk文件的位置position。mint即该series在该chunk里第一个sample的时间戳,maxt即该series在该chunk里最后一个sample的时间戳。
只有第一个mint保存的是原始时间戳值,后续的maxt以及后续chunk条目里的mintmaxt都是通过与前一个计算的差值进行保存。同样的差值编码也用于chunk条目的positon数值存储。

┌──────────────────────────────────────────────────────────────────────────┐
│ len <uvarint>                                                            │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │                     labels count <uvarint64>                         │ │
│ ├──────────────────────────────────────────────────────────────────────┤ │
│ │              ┌────────────────────────────────────────────┐          │ │
│ │              │ ref(l_i.name) <uvarint32>                  │          │ │
│ │              ├────────────────────────────────────────────┤          │ │
│ │              │ ref(l_i.value) <uvarint32>                 │          │ │
│ │              └────────────────────────────────────────────┘          │ │
│ │                             ...                                      │ │
│ ├──────────────────────────────────────────────────────────────────────┤ │
│ │                     chunks count <uvarint64>                         │ │
│ ├──────────────────────────────────────────────────────────────────────┤ │
│ │              ┌────────────────────────────────────────────┐          │ │
│ │              │ c_0.mint <varint64>                        │          │ │
│ │              ├────────────────────────────────────────────┤          │ │
│ │              │ c_0.maxt - c_0.mint <uvarint64>            │          │ │
│ │              ├────────────────────────────────────────────┤          │ │
│ │              │ ref(c_0.data) <uvarint64>                  │          │ │
│ │              └────────────────────────────────────────────┘          │ │
│ │              ┌────────────────────────────────────────────┐          │ │
│ │              │ c_i.mint - c_i-1.maxt <uvarint64>          │          │ │
│ │              ├────────────────────────────────────────────┤          │ │
│ │              │ c_i.maxt - c_i.mint <uvarint64>            │          │ │
│ │              ├────────────────────────────────────────────┤          │ │
│ │              │ ref(c_i.data) - ref(c_i-1.data) <varint64> │          │ │
│ │              └────────────────────────────────────────────┘          │ │
│ │                             ...                                      │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ CRC32 <4b>                                                               │
└──────────────────────────────────────────────────────────────────────────┘

注:每个block里会有多个chunk,每个series条目针对每个chunk都有且只有一个位置索引,由此也可以知道单个series在同一个chunk里的数据是连续存储的。

Label index 标签索引表

Label索引表保存了每个label name对应的values列表,可用于/api/v1/label/{name}/values接口快速查询。
每个Label索引section同样以len开始,以CRC32结束。数据区首先是字段name标识label name,然后是字段entries标识label下面的value个数,他们都是4字节大小。
随后是所有的label value,每个value都是4字节,和name一样,都是对Symbol Table的字符串的引用ID。value列表按字典序升序排序。

┌───────────────┬────────────────┬────────────────┐
│ len <4b>      │ #names <4b>    │ #entries <4b>  │
├───────────────┴────────────────┴────────────────┤
│ ┌─────────────────────────────────────────────┐ │
│ │ ref(value_0) <4b>                           │ │
│ ├─────────────────────────────────────────────┤ │
│ │ ...                                         │ │
│ ├─────────────────────────────────────────────┤ │
│ │ ref(value_n) <4b>                           │ │
│ └─────────────────────────────────────────────┘ │
│                      . . .                      │
├─────────────────────────────────────────────────┤
│ CRC32 <4b>                                      │
└─────────────────────────────────────────────────┘

如下例所示,为一个简单的label name对应4个value:

┌────┬───┬───┬──────────────┬──────────────┬──────────────┬──────────────┬───────┐
│ 24 │ 1 │ 4 │ ref(value_0) | ref(value_1) | ref(value_2) | ref(value_3) | CRC32 |
└────┴───┴───┴──────────────┴──────────────┴──────────────┴──────────────┴───────┘

Label索引表每个name的长度不同,无法根据name快速定位到values在索引表的位置。该工作由Label offset table标签偏移表完成,该表包含了每个label name对应在Label索引表中的位置。

Postings 倒排索引表

Postings表保存了每个label pair对应的series列表,这些series里都包含改label pair。series列表按ID递增排序。
每个Postings section包含字段lenentriesseries列表和CRC32

┌────────────────────┬────────────────────┐
│ len <4b>           │ #entries <4b>      │
├────────────────────┴────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ ref(series_1) <4b>                  │ │
│ ├─────────────────────────────────────┤ │
│ │ ...                                 │ │
│ ├─────────────────────────────────────┤ │
│ │ ref(series_n) <4b>                  │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ CRC32 <4b>                              │
└─────────────────────────────────────────┘

同样,通过label pair快速索引到倒排索引表中位置的工作由Postings offset table倒排索引偏移表完成。

Label offset table 标签偏移表

该表保存了一系列label偏移条目,每个条目包含label name和指向Label索引表的位置信息。

┌─────────────────────┬──────────────────────┐
│ len <4b>            │ #entries <4b>        │
├─────────────────────┴──────────────────────┤
│ ┌────────────────────────────────────────┐ │
│ │  n = 1 <1b>                            │ │
│ ├──────────────────────┬─────────────────┤ │
│ │ len(name) <uvarint>  │ name <bytes>    │ │
│ ├──────────────────────┴─────────────────┤ │
│ │  offset <uvarint64>                    │ │
│ └────────────────────────────────────────┘ │
│                    . . .                   │
├────────────────────────────────────────────┤
│  CRC32 <4b>                                │
└────────────────────────────────────────────┘

注:该表里的label name是直接保存了原始字符串,而不是对Symbol table的引用。

Postings offset table 倒排索引偏移表

该表保存了一系列label pair条目,每个条目包含label name/value pair和指向Postings表的位置信息。该表在index读取时会部分导入到内存中。

┌─────────────────────┬──────────────────────┐
│ len <4b>            │ #entries <4b>        │
├─────────────────────┴──────────────────────┤
│ ┌────────────────────────────────────────┐ │
│ │  n = 2 <1b>                            │ │
│ ├──────────────────────┬─────────────────┤ │
│ │ len(name) <uvarint>  │ name <bytes>    │ │
│ ├──────────────────────┼─────────────────┤ │
│ │ len(value) <uvarint> │ value <bytes>   │ │
│ ├──────────────────────┴─────────────────┤ │
│ │  offset <uvarint64>                    │ │
│ └────────────────────────────────────────┘ │
│                    . . .                   │
├────────────────────────────────────────────┤
│  CRC32 <4b>                                │
└────────────────────────────────────────────┘

注:该表里的label name/value也是直接存储了原始字符串,而不是对Symbol table的引用。

TOC 目录表

TOC(table of contents)目录表保存了指向各个表的位置信息,如果值为0,则表示对应的表不存在,对应的数据查询返回为空。

┌─────────────────────────────────────────┐
│ ref(symbols) <8b>                       │
├─────────────────────────────────────────┤
│ ref(series) <8b>                        │
├─────────────────────────────────────────┤
│ ref(label indices start) <8b>           │
├─────────────────────────────────────────┤
│ ref(label offset table) <8b>            │
├─────────────────────────────────────────┤
│ ref(postings start) <8b>                │
├─────────────────────────────────────────┤
│ ref(postings offset table) <8b>         │
├─────────────────────────────────────────┤
│ CRC32 <4b>                              │
└─────────────────────────────────────────┘

参考

https://github.com/prometheus/prometheus/blob/master/tsdb/docs/format/index.md

posted @ 2020-11-12 11:32  舰队  阅读(1080)  评论(0编辑  收藏  举报