<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TSDB on NoneBack</title><link>https://noneback.github.io/zh/tags/tsdb/</link><description>Recent content in TSDB on NoneBack created by</description><generator>Hugo -- gohugo.io</generator><language>zh</language><copyright>@NoneBack All rights reserved</copyright><lastBuildDate>Tue, 31 Dec 2024 01:10:28 +0800</lastBuildDate><atom:link href="https://noneback.github.io/zh/tags/tsdb/index.xml" rel="self" type="application/rss+xml"/><item><title>Prometheus--TSDB</title><link>https://noneback.github.io/zh/blog/zh/prometheus-tsdb/</link><pubDate>Tue, 31 Dec 2024 01:10:28 +0800</pubDate><guid>https://noneback.github.io/zh/blog/zh/prometheus-tsdb/</guid><description>&lt;p&gt;最近晋升，顺便总结一些以前的工作。工作里大规模数据库可观测系统占了很大一部分，不过这个和云原生监控系统Prometheus还是很不一样的。现在来学习一下开源监控的标准系统。&lt;/p&gt;
&lt;p&gt;文章主要涉及 Prometheus 自带的单机时序存储。简单写写里面的TSDB设计，不涉及源码分析。&lt;/p&gt;
&lt;p&gt;这种项目的源码分析性价比有点低,除非我是专门搞tsdb的，不然分析之后也容易忘，再说了也不是什么很好的代码。&lt;/p&gt;
&lt;h2 id="数据查询模型"&gt;数据+查询模型&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一个&lt;/strong&gt;监控指标被描述为一组与时间有关的数据结构，timeseries。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;$$
{timeseries} = \quad\lbrace \quad metric(attached\ with\ a\ set\ of\ labels) \Rightarrow (t_0,\ v_0),\ (t_1,\ v_1),\ (t_2,\ v_2),\ \ldots,\ (t_n, v_n) \quad\rbrace
$$&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;查询使用 ${identifier(metric\ +\ sets\ of\ selected\ labels\ value)}$ 查询得到对应的timeseries&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;series
^
│ . . . . . . . . . . . . . . . . . . . . . . {__name__=&amp;#34;request_total&amp;#34;, method=&amp;#34;GET&amp;#34;}
│ . . . . . . . . . . . . . . . . . . . . . . {__name__=&amp;#34;request_total&amp;#34;, method=&amp;#34;POST&amp;#34;}
│ . . . . . . .
│ . . . . . . . . . . . . . . . . . . . ...
│ . . . . . . . . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . . . . . {__name__=&amp;#34;errors_total&amp;#34;, method=&amp;#34;POST&amp;#34;}
│ . . . . . . . . . . . . . . . . . {__name__=&amp;#34;errors_total&amp;#34;, method=&amp;#34;GET&amp;#34;}
│ . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . . . ...
│ . . . . . . . . . . . . . . . . . . . .
v
&amp;lt;-------------------- time ---------------------&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;identifier 的定义很重要。否则 ts 数据容易膨胀，比如容器重建的场景，如果 label 用的不好很容易膨胀。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="数据组织方式"&gt;数据组织方式&lt;/h2&gt;
&lt;p&gt;对云原生的场景，监控数据有哪些特点？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据的生命周期短。因为单个容器本身的生命周期就很短（比如扩缩容场景，大量临时任务等场景），timeseries 很容易在一定时间维度上膨胀。&lt;/li&gt;
&lt;li&gt;垂直写入，水平查询&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/noneback/images/picgo/20241231010014.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;带着这些问题看看 数据文件的组织方法，如何解决或者避免这些问题。&lt;/p&gt;
&lt;p&gt;那么首先看看逻辑的结构&lt;/p&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/noneback/images/picgo/20241231010031.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;整个 db 由 blocks + HEAD 组成，block 又可以进一步被拆分成 chunks，HEAD 基本上就是内存数据+wal 保障的读写 buffer 区域。chunks 包含多种 timeseries。&lt;/p&gt;
&lt;p&gt;对于单个 Block 的磁盘目录结构如下：&lt;/p&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;├── 01BKGV7JC0RY8A6MACW02A2PJD // block 的 ULID
│ ├── chunks
│ │ └── 000001
│ ├── tombstones
│ ├── index
│ └── meta.json
├── chunks_head
│ └── 000001
└── wal
├── 000000002
└── checkpoint.00000001
└── 00000000&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;block，一个时间段内（默认 2 小时）的所有数据，只读，用 &lt;a href="https://github.com/oklog/ulid"&gt;ULID&lt;/a&gt; 命名。每一个 block 内主要包括：
&lt;ul&gt;
&lt;li&gt;chunks 固定大小（最大 128M）的 chunks 文件&lt;/li&gt;
&lt;li&gt;index 索引文件，主要包含倒排索引的信息&lt;/li&gt;
&lt;li&gt;meta.json 元信息，主要包括 block 的 minTime/maxTime，用于在查询的时候data skip。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;chunks_head，当前在写入的 block 对应的 chunks 文件，只读，最多 120 个数据点，时间跨度最大 2 小时。&lt;/li&gt;
&lt;li&gt;wal： 数据完整性保证&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;从图上可以知道很多信息，比如wal保障数据完整性；Head 类似 tsdb 的 buffer pool，只不过用 mmap 托管，实现内存数据批量刷盘。Head 到达一定条件（比如时间阈值 ，数据大小 阈值） 之后 变成 immutable 的结构（block）刷盘。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;整体在数据组织上有很多概念和lsm 类存储结构类似。LSM结构也确实很适合 tsdb。&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;不难看出 Prometheus 的设计思路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过数据按时间分片的方式来解决数据生命周期短的问题&lt;/li&gt;
&lt;li&gt;通过内存攒批的方式来对应只写最新数据的场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;抛开与 leveldb 类似的地方不谈，我们来看看有什么不一样的地方。&lt;/p&gt;
&lt;p&gt;首先，使用的模型上不一样。leveldb 是 kv ，tsdb 是 timeseries，与时间强关联，而时间是单调递增的。很少会去写入过去时间端的数据。其次，查询的模型也不一样。tsdb 会有更多样话的查询方式，比如对label 的各种集合操作对 timeseries 进行过滤 ，所以会有更多的元信息来标注 timeseries，以便高效的查询。&lt;/p&gt;
&lt;p&gt;因为这类的需求，这里引入了一些新结构和功能：倒排索引，checkpoint，tombstone，retention，以及和 lsm kv模型不一样的 compaction 设计。这些让我们和对应的文件 format 一起分析。&lt;/p&gt;
&lt;h3 id="文件组织格式"&gt;文件组织格式&lt;/h3&gt;
&lt;p&gt;让我们来看看有哪些成分，具体的组织方式不是本文重点。&lt;/p&gt;
&lt;h4 id="metajson"&gt;meta.json&lt;/h4&gt;
&lt;p&gt;block 的一些信息，关键看 compaction 和 minT,maxT。&lt;/p&gt;
&lt;p&gt;minT,maxT 记录block 的时间访问，查询的时候可以用来做 data skip。&lt;/p&gt;
&lt;p&gt;companction：记录了block的历史信息，比如经历了几次 compaction(level), 由哪些 block 派生出来(source)。暂时不知道有啥用。可能可以compaction 或者 retention 的时候用到，比如清理重复的 source 里面的 block。&lt;/p&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{
&amp;#34;ulid&amp;#34;: &amp;#34;01EM6Q6A1YPX4G9TEB20J22B2R&amp;#34;, #
&amp;#34;minTime&amp;#34;: 1602237600000,
&amp;#34;maxTime&amp;#34;: 1602244800000,
&amp;#34;stats&amp;#34;: {
&amp;#34;numSamples&amp;#34;: 553673232,
&amp;#34;numSeries&amp;#34;: 1346066,
&amp;#34;numChunks&amp;#34;: 4440437
},
&amp;#34;compaction&amp;#34;: {
&amp;#34;level&amp;#34;: 1,
&amp;#34;sources&amp;#34;: [
&amp;#34;01EM65SHSX4VARXBBHBF0M0FDS&amp;#34;,
&amp;#34;01EM6GAJSYWSQQRDY782EA5ZPN&amp;#34;
]
},
&amp;#34;version&amp;#34;: 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id="chunks"&gt;chunks&lt;/h4&gt;
&lt;p&gt;普通的数据文件，它的索引都在 index 文件里。需要注意的是一个chunk 只能属于一个 timeseries，一个 timeseries 由多个 chunks组成。&lt;/p&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌──────────────────────────────┐
│ magic(0x85BD40DD) &amp;lt;4 byte&amp;gt; │
├──────────────────────────────┤
│ version(1) &amp;lt;1 byte&amp;gt; │
├──────────────────────────────┤
│ padding(0) &amp;lt;3 byte&amp;gt; │
├──────────────────────────────┤
│ ┌──────────────────────────┐ │
│ │ Chunk 1 │ │
│ ├──────────────────────────┤ │
│ │ ... │ │
│ ├──────────────────────────┤ │
│ │ Chunk N │ │
│ └──────────────────────────┘ │
└──────────────────────────────┘
Every Chunk:
┌───────────────┬───────────────────┬──────────────┬────────────────┐
│ len &amp;lt;uvarint&amp;gt; │ encoding &amp;lt;1 byte&amp;gt; │ data &amp;lt;bytes&amp;gt; │ CRC32 &amp;lt;4 byte&amp;gt; │
└───────────────┴───────────────────┴──────────────┴────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id="tombstone"&gt;tombstone&lt;/h4&gt;
&lt;p&gt;标记删除，tsdb 有时候会有一些删除操作。比如，短时 Job、容器销毁之后，业务上可能希望删除。标记删除主要是为了用追加写代替原地修改。后续可以通过 Compaction 回收这部分磁盘。&lt;/p&gt;
&lt;p&gt;当然不删除也没事，会有 TTL 到期删除磁盘过期数据。&lt;/p&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌────────────────────────────┬─────────────────────┐
│ magic(0x0130BA30) &amp;lt;4b&amp;gt; │ version(1) &amp;lt;1 byte&amp;gt; │
├────────────────────────────┴─────────────────────┤
│ ┌──────────────────────────────────────────────┐ │
│ │ Tombstone 1 │ │
│ ├──────────────────────────────────────────────┤ │
│ │ ... │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Tombstone N │ │
│ ├──────────────────────────────────────────────┤ │
│ │ CRC&amp;lt;4b&amp;gt; │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
Every Tombstone:
┌────────────────────────┬─────────────────┬─────────────────┐
│ series ref &amp;lt;uvarint64&amp;gt; │ mint &amp;lt;varint64&amp;gt; │ maxt &amp;lt;varint64&amp;gt; │
└────────────────────────┴─────────────────┴─────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id="index-文件"&gt;index 文件&lt;/h4&gt;
&lt;p&gt;包含了读取用到的所有信息。比如倒排索引，timeseries 和 chunks 的映射关系。&lt;/p&gt;
&lt;p&gt;里面值得注意的是 Series 和 Postings 两类。&lt;/p&gt;
&lt;p&gt;Series 里面记录了Blocks里所有 series 对应 chunks的信息。&lt;/p&gt;
&lt;p&gt;Posting Offset Table记录了各个倒排索引的位置。倒排索引具体的内容被放在了 Postings里。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对倒排索引的集合操作，可以快速过滤出来符合条件的 timeseries&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="code-block" data-language="text"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;text&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="复制"&gt;复制&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌────────────────────────────┬─────────────────────┐
│ magic(0xBAAAD700) &amp;lt;4b&amp;gt; │ version(1) &amp;lt;1 byte&amp;gt; │
├────────────────────────────┴─────────────────────┤
│ ┌──────────────────────────────────────────────┐ │
│ │ Symbol Table │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Series │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Label Index 1 │ │
│ ├──────────────────────────────────────────────┤ │
│ │ ... │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Label Index N │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Postings 1 │ │
│ ├──────────────────────────────────────────────┤ │
│ │ ... │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Postings N │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Label Offset Table │ │
│ ├──────────────────────────────────────────────┤ │
│ │ Postings Offset Table │ │
│ ├──────────────────────────────────────────────┤ │
│ │ TOC │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
A Series:
┌──────────────────────────────────────────────────────┐
│ len &amp;lt;uvarint&amp;gt; │
├──────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────┐ │
│ │ labels count &amp;lt;uvarint64&amp;gt; │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ ref(l_i.name) &amp;lt;uvarint32&amp;gt; │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ ref(l_i.value) &amp;lt;uvarint32&amp;gt; │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ ... │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ chunks count &amp;lt;uvarint64&amp;gt; │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ c_0.mint &amp;lt;varint64&amp;gt; │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ c_0.maxt - c_0.mint &amp;lt;uvarint64&amp;gt; │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ ref(c_0.data) &amp;lt;uvarint64&amp;gt; │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ c_i.mint - c_i-1.maxt &amp;lt;uvarint64&amp;gt; │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ c_i.maxt - c_i.mint &amp;lt;uvarint64&amp;gt; │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ ref(c_i.data) - ref(c_i-1.data) &amp;lt;varint64&amp;gt; │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ ... │ │
│ └──────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────┤
│ CRC32 &amp;lt;4b&amp;gt; │
└──────────────────────────────────────────────────────┘
A Postings:
┌────────────────────┬────────────────────┐
│ len &amp;lt;4b&amp;gt; │ #entries &amp;lt;4b&amp;gt; │
├────────────────────┴────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ ref(series_1) &amp;lt;4b&amp;gt; │ │
│ ├─────────────────────────────────────┤ │
│ │ ... │ │
│ ├─────────────────────────────────────┤ │
│ │ ref(series_n) &amp;lt;4b&amp;gt; │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ CRC32 &amp;lt;4b&amp;gt; │
└─────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="加速磁盘查询"&gt;加速磁盘查询&lt;/h2&gt;
&lt;p&gt;这里重点讲述一次查询是如何找到符合条件的数据的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先在 Posting Offset Table 中，找到对应 label 的 Postings 位置&lt;/li&gt;
&lt;li&gt;根据 Postings 中的 series 信息，找到对应的 chunk 位置，即上文中的 series ref&lt;/li&gt;
&lt;li&gt;根据timeseries 找到对应的 chunks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/noneback/images/picgo/20241231005912.png" alt="" loading="lazy" decoding="async"&gt;
&lt;img src="https://github.com/noneback/images/blob/picgo/image.png?raw=true" alt="!https://img.alicdn.com/imgextra/i4/581166664/O1CN01HsQNy31z6A3HT9l5B_!!581166664.jpg" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="compaction"&gt;Compaction&lt;/h2&gt;
&lt;p&gt;类似 leveldb，也有 major compaction和 minor compaction。在这里叫 Compaction 和 Head Compaction。&lt;/p&gt;
&lt;p&gt;Head Compaction类似把 Head部分持久化成 Chunk 的过程，在这个过程中，mark delete 的tombstone 会被真的删除（毕竟要读到内存里，顺手删了）。&lt;/p&gt;
&lt;p&gt;Compaction 就是把 Block 合并的一个过程。这个动作可以回收：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;标记删除占用的磁盘资源。&lt;/li&gt;
&lt;li&gt;重复的部分信息。比如散落在多个 block 里面的重复 chunk， 倒排索引这类的磁盘记录&lt;/li&gt;
&lt;li&gt;加速一些查询的处理。比如数据在多个数据块之间overlapping（没处理的话，读出来之后要在内存里处理，不如顺便在 compaction 的时候处理掉，合并之后读取也方便了）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="什么时候会-compaction"&gt;什么时候会 compaction？&lt;/h3&gt;
&lt;p&gt;官方博客里没咋说清楚，只提到了data overlapping 的时候会触发。 不过其他策略也挺多的。比如定时触发，每次 minor 触发的时候判断，基于 tombstone 的大小判断，手动触发这类。可以参考一些 leveldb 的策略。&lt;/p&gt;
&lt;h2 id="retention"&gt;Retention&lt;/h2&gt;
&lt;p&gt;没啥说的，就是 Time or Size Based TTL，感觉放到 Compaction 里面做也不是不行。&lt;/p&gt;
&lt;h2 id="snapshot"&gt;Snapshot&lt;/h2&gt;
&lt;p&gt;感觉就是把内存数据 dump 一份到磁盘上而已……。可能是为了平衡大量指标数据磁盘写入以及数据完整性。不然有了 wal 还要它干啥。&lt;/p&gt;
&lt;h2 id="reference"&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20210803115658/https://fabxc.org/tsdb/"&gt;https://web.archive.org/web/20210803115658/https://fabxc.org/tsdb/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://liujiacai.net/blog/2021/04/11/prometheus-storage-engine/"&gt;https://liujiacai.net/blog/2021/04/11/prometheus-storage-engine/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.qimao.com/prometheus-tsdb-de-she-ji-yu-shi-xian-2/"&gt;https://tech.qimao.com/prometheus-tsdb-de-she-ji-yu-shi-xian-2/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ganeshvernekar.com/blog/prometheus-tsdb-persistent-block-and-its-index/"&gt;https://ganeshvernekar.com/blog/prometheus-tsdb-persistent-block-and-its-index/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ganeshvernekar.com/blog/prometheus-tsdb-compaction-and-retention/#compaction"&gt;https://ganeshvernekar.com/blog/prometheus-tsdb-compaction-and-retention/#compaction&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>