• 【博客479】prometheus-----时序数据模型及其存储机制


    prometheus-----时序数据及其写入过程

    prometheus文件结构

    在这里插入图片描述

    数据模型 DataModel

    Prometheus 的监控数据以指标(metric)的形式保存在内置的时间序列数据库(TSDB)当中。

    1、指标名称和标签(metric names, labels)

    每一条时间序列由指标名称(Metrics Name)以及一组标签labels(键值KV对)唯一标识。
    TIPS: 改变标签中的K或者V值(包括添加或删除),都会创建新的时间序列。

    2、样本Samples

    在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

    • 指标(metric):指标名称Metric names和标签labels;
    • 时间戳(timestamp):一个精确到毫秒的时间戳;
    • 样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。

    如下就是三个样本数据:

    <--------------------------- metric ------------------------><-----timestamp-----><–value–>
    <—metric name–><--------------labels--------------><------timestamp------><–value–>
    http_request_total{status=“200”, method=“GET”}@1434417560938 => 94355
    http_request_total{status=“200”, method=“GET”}@1434417561287 => 94334
    http_request_total{status=“200”, method=“POST”}@1434417561287 => 93656

    当我们根据时间顺序,把样本数据统一放在一起,就可以形成一条监控曲线,如下:

    在这里插入图片描述
    3、Series

    这样的特定的metric、timestamp、value构成的时间序列(TimeSeries) 在Prometheus中被称作Series。

    4、总结:

    Metric names 指标名称
    labels 指标标签,与指标名称一同构成唯一标识项
    Samples 一个时间点的 Metric names,labels,样本值(value),
    Series 由很多相关的samples组成的时间序列

    时间序列 (Time Series) 指的是某个指标随时间变化的所有历史数据,而样本 (Sample) 指的是历史数据中该变量的瞬时值。
    当我们有很多的Series,就可以用下面的示意图表示了,图上每一个点都是一个Sample。

    在这里插入图片描述

    存储机制

    存储机制:

    Prometheus将最近的数据保存在内存中,这样查询最近的数据会变得非常快,然后通过一个compactor定时将数据打包到磁盘。数据在内存中最少保留2个小时(storage.tsdb.min-block-duration。为了防止程序崩溃导致数据丢失,实现了WAL(write-ahead-log)机制,启动时会以写入日志(WAL)的方式来实现重播,从而恢复数据。

    Block:

    一个Block就是一个独立的小型数据库,其保存了一段时间内所有查询所用到的信息。包括标签/索引/符号表数据等等。Block的实质就是将一段时间里的内存数据组织成文件形式保存下来。

    在这里插入图片描述
    在这里插入图片描述

    最近的Block一般是存储了2小时的数据,而较为久远的Block则会通过compactor进行合并,一个Block可能存储了若干小时的信息。值得注意的是,合并操作只是减少了索引的大小(尤其是符号表的合并),而本身数据(chunks)的大小并没有任何改变。

    Block还可以理解,每两个小时数据切一个块,就是一个Block,chunk这个概念是哪里来的?

    在Prometheus内部,chunk是用来存储压缩后的样本sample数据的最小单位。chunk的大小1KB,每个chunk可以存储最多120个样本,当chunk存满120个样本或超过了chunkRange的时间范围(默认情况为 2h),就会写到新的chunk里。

    每一个Series的样本数据,由多个chunk来进行存储,按照上面所说的存满120个或者超过chunkRange时间,就会写到新的里面,chunk存储的是经过压缩后的sample数据在这里插入图片描述

    每个 block 实际上就是一个小型数据库,内部存储着该时间窗口内的所有时序数据,因此它需要拥有自己的 index 和 chunks。除了最新的、正在接收新鲜数据的 block 之外,其它 blocks 都是不可变的。由于新数据的写入都在内存中,数据的写效率较高:
    在这里插入图片描述
    为了防止数据丢失,所有新采集的数据都会被写入到 WAL 日志中,在系统恢复时能快速地将其中的数据恢复到内存中。在查询时,我们需要将查询发送到不同的 block 中,再将结果聚合。

    按时间将数据分片赋予了存储引擎新的能力:

    • 当查询某个时间范围内的数据,我们可以直接忽略在时间范围外的 blocks
    • 写完一个 block 后,我们可以将轻易地其持久化到磁盘中,因为只涉及到少量几个文件的写入
    • 新的数据,也是最常被查询的数据会处在内存中,提高查询效率 (第二代同样支持)
    • 每个 chunk 不再是固定的 1KB 大小,我们可以选择任意合适的大小,选择合适的压缩方式
    • 删除超过留存时间的数据变得异常简单,直接删除整个文件夹即可

    Compaction

    将所有时序数据按时间切割成许多 blocks,当新写满的 block 持久化到磁盘后,相应的 WAL 文件也会被清除。写入数据时,我们希望每个 block 不要太大,比如 2 小时左右,来避免在内存中积累过多的数据。读取数据时,若查询涉及到多个时间段,就需要对许多个 block 分别执行查询,然后再合并结果。假如需要查询一周的数据,那么这个查询将涉及到 80 多个 blocks,降低数据读取的效率。

    找一个block的mata.json文件:

    	"ulid":"01EXTEH5JA3QCQB0PXHAPP999D",
    	// maxTime - maxTime =>162h
    	"minTime":1610964800000,
    	"maxTime":1611548000000
    	......
    	"compaction":{
    		"level": 5,
    		"sources: [
    			31个01EX......
    		]
    	},
    	"parents: [
    		{	
    			"ulid": 01EXTEH5JA3QCQB1PXHAPP999D
    			...
    		}
    		{	
    			"ulid": 01EXTEH6JA3QCQB1PXHAPP999D
    			...
    		}
    				{	
    			"ulid": 01EXTEH5JA31CQB1PXHAPP999D
    			...
    		}
    	]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    从中我们可以看到,该Block是由31个原始Block经历5次压缩而来。最后一次压缩的三个Block ulid记录在parents中。如下图所示:
    在这里插入图片描述

    为了既能写得快,又能读得快,我们就得引入 compaction,后者将一个或多个 blocks 中的数据合并成一个更大的 block,在合并的过程中会自动丢弃被删除的数据、合并多个版本的数据、重新结构化 chunks 来优化查询效率

    Retention

    当数据超过留存时间时,删除旧数据非常容易: 在这里插入图片描述
    直接删除在边界之外的 block 文件夹即可。如果边界在某个 block 之内,则暂时将它留存,知道边界超出为止。当然,在 Compaction 中,我们会将旧的 blocks 合并成更大的 block;在 Retention 时,我们又希望能够粒度更小。所以 Compaction 与 Retention 的策略之间存在着一定的互斥关系。Prometheus 的系统参数可以对单个 block 的大小作出限制,来寻找二者之间的平衡。

    数据的写入

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    当 Head 中的数据 spanchunkRange*3/2时,也就是存了3h后,第一个chunkRange数据(此处为 2h)被压缩成一个持久块,此时 WAL 被截断并创建了一个检查点。

    磁盘上的数据格式

    head block 与persistent block

    Head block:

    上面介绍的 mmap 中的 chunks 保存在名为 chunks_head 的目录下, 文件序列与 WAL 中的相似. 如下图:

    在这里插入图片描述

    文件格式:

    ┌──────────────────────────────┐
    │  magic(0x0130BC91) <4 byte>  │
    ├──────────────────────────────┤
    │    version(1) <1 byte>       │
    ├──────────────────────────────┤
    │    padding(0) <3 byte>       │
    ├──────────────────────────────┤
    │ ┌──────────────────────────┐ │
    │ │         Chunk 1          │ │
    │ ├──────────────────────────┤ │
    │ │          ...             │ │
    │ ├──────────────────────────┤ │
    │ │         Chunk N          │ │
    │ └──────────────────────────┘ │
    └──────────────────────────────┘
    
    magic number:是可以唯一标识一个文件时 head_chunk 的数字
    version:告诉我们如何解码文件中的 chunks
    padding:是为了将来可能需要的选项而预留出来的
    
    一个 chunk 的格式如下所示:
    ┌─────────────────────┬───────────────────────┬───────────────────────┬───────────────────┬───────────────┬──────────────┬────────────────┐
    | series ref <8 byte> | mint <8 byte, uint64> | maxt <8 byte, uint64> | encoding <1 byte> | len  | data  │ CRC32 <4 byte> │
    └─────────────────────┴───────────────────────┴───────────────────────┴───────────────────┴───────────────┴──────────────┴────────────────┘
    
    series ref 是用于访问内存中的序列的 id, 即上文中 mmap 中的引用
    mint 是该 chunk 中 series 的最小时间戳
    max 是该 chunk 中 series 的最大时间戳
    encoding 是压缩该 chunk 时使用的编码
    len 是此后的字节数
    data 是压缩后的数据
    CRC32 是用于检查数据完整性的校验和
    
    Head Block 通过 series ref, 以及 mint、maxt 就可以实现不访问磁盘就选择 chunk.
    
    其中, ref 是 8 bytes, 前 4 个字节告诉了 chunk 存在哪个文件中(file number),
     后四个字节告诉了 chunk 在该文件中的偏移量.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    persistent block:
    在这里插入图片描述

    文件格式:见这里的具体分析
    【博客478】prometheus-----存储目录结构以及格式,作用分析

    此处只讲head Block 与persistent block的chunk文件格式区别:

    与 head_chunk 差不多, 区别是少了 series ref、mint、maxt。
    在 Head_chunk 中需要这些附加信息是为了 prometheus 重启时能够创建内存索引,
    但是在持久化 block 中, 这些信息在 index 文件中存储了, 因此不再需要.
    同样通过 reference 来访问这些 chunk.

    ┌──────────────────────────────┐
    │  magic(0x85BD40DD) <4 byte>  │
    ├──────────────────────────────┤
    │    version(1) <1 byte>       │
    ├──────────────────────────────┤
    │    padding(0) <3 byte>       │
    ├──────────────────────────────┤
    │ ┌──────────────────────────┐ │
    │ │         Chunk 1          │ │
    │ ├──────────────────────────┤ │
    │ │          ...             │ │
    │ ├──────────────────────────┤ │
    │ │         Chunk N          │ │
    │ └──────────────────────────┘ │
    └──────────────────────────────┘
    
    ┌───────────────┬───────────────────┬──────────────┬────────────────┐
    │ len  │ encoding <1 byte> │ data  │ CRC32 <4 byte> │
    └───────────────┴───────────────────┴──────────────┴────────────────┘
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    WAL 中checkpoint的作用

    在这里插入图片描述
    在这里插入图片描述

    分块存储

    在现在的 TSDB 存储方案中,TSDB 的数据被根据时间段分割成一个个目录,每个目录内部都放着完整的一段时间的数据,每个目录的数据时间是不重叠的。

    合并规则:
    在这里插入图片描述
    好处:

    这样的好处就是存取,删除都很方便。如果要查询数据,那么可以根据查询的时间按需读取部分数据,而不用每次都读取全部目录的数据;并且,如果要删除多久以前的数据,那么直接删除对应时间段的目录就可以了,而不需要删除的目录根本不需要改变,就是这么方便。

    不足:

    这里有一个隐含的缺陷,那就是这里说了 50 小时的目录之后就不压缩了,如果你存放的数据太久的话,迟早也是 too many open file 的,Prometheus 官方对此的解释是,你不要用我原生的存储存放太久的数据(默认的存放周期是 15 天,超过 15 天的数据会被定期清除)。如果你想存放长期的数据,那么请使用通过 Remote Storage 使用第三方存储软件来存放。

  • 相关阅读:
    校物联网智慧安全用电平台的设计和运用-安科瑞黄安南
    长事务 (Long Transactions)
    自己用操作系统四元素:进程线程管理、内存管理、文件系统和驱动一起拼一个RTOS
    YOLOv7改进:小目标遮挡物性能提升(SEAM、MultiSEAM),涨点神器!!!
    gRPC-Java
    安装Rocky 9
    LeetCode 2342. 数位和相等数对的最大和 哈希
    两个宝藏|关于我在github上冲浪时的一个小技巧。
    [附源码]Python计算机毕业设计Django校园运动会管理系统
    【C语言】冒泡排序
  • 原文地址:https://blog.csdn.net/qq_43684922/article/details/126689923