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條目里的mint
和maxt
都是通過與前一個計算的差值進行保存。同樣的差值編碼也用於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包含字段len
,entries
,series
列表和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