Promethues TSDB (Part 1): The Head Block
本文译自Ganesh Vernekar 的 prometheus-tsdb-the-head-block。
文章目录
Introduction
Promethues 2.0 已经启动三年时间,除Fabian的 Writing a Time Series Database from Scratch
之外,没有太多资料是介绍Prometheus TSDB部分的,而Prometheus官方的 Formt 的文档也更多是面向开发者的。
Promtheus的TSDB近期吸引了很多新的贡献者,但是因为资料的缺失,导致大家对TSDB的理解依然是一大痛点。因为,我计划通过一系列的博客来介绍TSDB的工作原理,同时也附上一些相关的代码引用,以便于Contributors理解TSDB的原理。
在本篇博客中,我主要讨论TSDB中的in-memory部分 - Head Block,在后续的博客,我还会再继续深入的介绍其它的组件,如WAL&CheckPointing、Chunk memory-mapping的设计、压缩、持久化Block、索引,以及Block快照等。
Prologue
Fabian的博客对于理解数据模型、核心概念、TSDB的设计等是很好的阅读材料,同时他也在2017的 PromCon 上发表过有关于此的演讲,我强烈建议先阅读下他的博客或观看演讲视频,以对这些概念有初步的理解。
在本篇博客里所讲解的关于样本的生命周期相关内容,在我 KubeCon 的演讲中也有介绍。
Small Overview of TSDB
如上图所示,Head Block(黄色块)是in-memory的部分,而Block N(灰色块)则是持久化到磁盘上的不可变的部分。我们通过Write-Ahead-Log(WAL,即棕色块)来实现持久化的写入。对于一个待插入的样本数据(粉色块),首先会进入Head Block,并在内存中驻留一段时间,之后很快就会被刷新到磁盘以及memory-mapped(蓝色块)中。之后到某个确定的时刻时,memory-mapped Chunk或in-memory Chunk中的数据就会“变老”,这时它们就会被作为一个持久化的块存储到磁盘上。更早之前的多个Block会被合并,最终如果这些Block超过了保留周期的,则会被删除。
Life Of a Sample in the Head
如下的所有论述都是讨论单个时序,对所有时序也都是同样的道理。
样本数据都是存储在一个名为“Chunk”的压缩单元中,当一个样本数据到达时,它会被当前活跃的Chunk接收(红色块),这也是唯一我们可以写入数据的单元。
当往Chunk提交样本数据的时候,出于持久化的目的,我们也会通过Write-Ahead-Log(WAL,即棕色块)的方式将其落盘,这意味着即便机器突然Crash,我们也可以通过WAL的数据来恢复in-memory的数据。后续我会在别的博客中单独讲解WAL的内容。
当Chunk被塞满数据时(默认120个样本),或者其已经到达Chunk/Block的最大时间窗口(默认2小时,后续称之为chunkRange),一个新的Chunk会被切分出来,并且当前这个老的Chunk会被打上“full”的标签。我们暂时假设我们抓取样本数据的间隔是15s,所以120个样本大约30分钟就能抓满。上面编号为1的“full Chunk”(黄色块)代表老的Chunk,红色块则是新切分出来的Chunk。
从Prometheus v2.19.0开始,我们不再在内存中存储所有Chunk了,当新的Chunk切分出来时,老的Chunk就会很快的写入磁盘,memory-mapped中只会存储一个引用。通过memory-mapping,我们可以通过这个引用动态的将Chunk加载到内存中。
同理,当不断有新的样本数据采集进来的时候,新的Chunk也会持续不断的被切分出来。
然后它们会被刷到磁盘并memory-mapped。
经过一段时间后,Head Block就会如上图所示,如果我们考虑当新的Chunk(红色块)快满的情况的话,那此时存储了块3个小时的数据(6个Chunk,每个Chunk按30分钟采满计算)。
当Head Block中的数据超过chunkRange*3/2
的范围后,最早的chunkRange(两小时)周期内的数据将被压缩存储到持久化的Block中。如果你有注意WAL(棕色快)的变化,则会发现此时WAL的部分数据会被截断,并且一个“checkpoint”会被创建(未在此图中示意)。我会在后续的博客中详细介绍checkpointing、WAL截断、压缩、持久化Block以及索引等机制。
这种采集样本、memory-mapping、压缩到持久化Block的过程一直在循环持续,这也构成了Head Block的基本功能。
Few more things to note/understand
Where is the index?
作为倒排索引的形式存储在内存中,在Fabian的博客中有阐述整体的概念。当Head Block触发持久化到磁盘时,Head Block中老的Chunk会被截断,此时索引的垃圾回收也会将Head中不存在的数据的索引移除。
Handling Restarts
当TSDB需要重启时(正常或突发情况),它通过磁盘上的memory-mapped Chunks以及WAL来获取数据和时间,并在内存中重构索引和Chunk。
Code reference
tsdb/db.go
TSDB的整体功能。
此外与本篇博客相关性较高的是tsdb/head.go
,包含in-memory Chunks的主要逻辑,可以看做WAL和memory-mapping的黑盒。