zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    浅谈图标布局
    和浏览器异步请求取消相关的那些事
    chrome浏览器的跨域设置——包括版本49前后两种设置
    cordova加载层、进度条、文件选择插件
    js构建ui的统一异常处理方案(四)
    js构建ui的统一异常处理方案(三)
    通过 IntelliJ IDEA 来 Debug Jar包
    JPA使用Specification like查询时特殊字符%和_处理问题 Escape示例
    java8新特性:利用Lambda处理List集合
    让开发部署提速的 IDEA 插件神器攻略(转)
  • 原文地址:https://www.cnblogs.com/jimbo17/p/13963028.html
Copyright © 2011-2022 走看看