Prometheus TSDB存储原理

article/2025/9/2 20:46:51

Python微信订餐小程序课程视频

https://blog.csdn.net/m0_56069948/article/details/122285951

Python实战量化交易理财系统

https://blog.csdn.net/m0_56069948/article/details/122285941
Prometheus 包含一个存储在本地磁盘的时间序列数据库,同时也支持与远程存储系统集成,比如grafana cloud 提供的免费云存储API,只需将remote_write接口信息填写在Prometheus配置文件即可。

image-20220412141006992

本文不涉及远程存储接口内容,主要介绍Prometheus 时序数据的本地存储实现原理。

什么是时序数据?


在学习Prometheus TSDB存储原理之前,我们先来认识一下Prometheus TSDB、InfluxDB这类时序数据库的时序数据指的是什么?

时序数据通常以(key,value)的形式出现,在时间序列采集点上所对应值的集,即每个数据点都是一个由时间戳和值组成的元组。

identifier->(t0,v0),(t1,v1),(t2,v2)...

Prometheus TSDB的数据模型

<metric name>{<label name>=<label value>, ...} 

具体到某个实例中

requests\_total{method="POST", handler="/messages"}  

在存储时可以通过name label来标记metric name,再通过标识符@来标识时间,这样构成了一个完整的时序数据样本。

 ----------------------------------------key-----------------------------------------------value---------
{__name__="requests\_total",method="POST", handler="/messages"}   @1649483597.197             52

一个时间序列是一组时间上严格单调递增的数据点序列,它可以通过metric来寻址。抽象成二维平面来看,二维平面的横轴代表单调递增的时间,metrics 遍及整个纵轴。在提取样本数据时只要给定时间窗口和metric就可以得到value

series

时序数据如何在Prometheus TSDB存储?


上面我们简单了解了时序数据,接下来我们展开Prometheus TSDB存储(V3引擎)

Prometheus TSDB 概览

image-20220413104124771

在上图中,Head 块是TSDB的内存块,灰色块Block是磁盘上的持久块。

首先传入的样本(t,v)进入 Head 块,为了防止内存数据丢失先做一次预写日志 (WAL),并在内存中停留一段时间,然后刷新到磁盘并进行内存映射(M-map)。当这些内存映射的块或内存中的块老化到某个时间点时,会作为持久块Block存储到磁盘。接下来多个Block在它们变旧时被合并,并在超过保留期限后被清理。

Head中样本的生命周期

image-20220413120050962

当一个样本传入时,它会被加载到Head中的active chunk(红色块),这是唯一一个可以主动写入数据的单元,为了防止内存数据丢失还会做一次预写日志 (WAL)

image-20220413120803681

一旦active chunk被填满时(超过2小时或120样本),将旧的数据截断为head_chunk1。

image-20220413121223066

head_chunk1被刷新到磁盘然后进行内存映射。active chunk继续写入数据、截断数据、写入到内存映射,如此反复。

image-20220413121732282

内存映射应该只加载最新的、最被频繁使用的数据,所以Prometheus TSDB将就是旧数据刷新到磁盘持久化存储Block,如上1-4为旧数据被写入到下图的Block中。

image-20220413113035412

此时我们再来看一下Prometheus TSDB 数据目录基本结构,好像更清晰了一些。

./data
├── 01BKGV7JBM69T2G1BGBGM6KB12    
│   └── meta.json
├── 01BKGTZQ1SYQJTR4PB43C8PD98   # block ID
│   ├── chunks  	   # Block中的chunk文件
│   │   └── 000001     
│   ├── tombstones     # 数据删除记录文件
│   ├── index          # 索引
│   └── meta.json	   # bolck元信息
├── chunks_head		   # head内存映射
│   └── 000001		  
└── wal			       # 预写日志├── 000000002	  └── checkpoint.00000001└── 00000000
WAL 中checkpoint的作用

我们需要定期删除旧的 wal 数据,否则磁盘最终会被填满,并且在TSDB重启时 replay wal 事件时会占用大量时间,所以wal中任何不再需要的数据,都需要被清理。而checkpoint会将wal 清理过后的数据做过滤写成新的段。

如下有6个wal数据段

data
└── wal├── 000000├── 000001├── 000002├── 000003├── 000004└── 000005

现在我们要清理时间点T之前的样本数据,假设为前4个数据段:

检查点操作将按000000 000001 000002 000003顺序遍历所有记录,并且:

  1. 删除不再在 Head 中的所有序列记录。
  2. 丢弃所有 time 在T之前的样本。
  3. 删除T之前的所有 tombstone 记录。
  4. 重写剩余的序列、样本和tombstone记录(与它们在 WAL 中出现的顺序相同)。

checkpoint被命名为创建checkpoint的最后一个段号checkpoint.X

这样我们得到了新的wal数据,当wal在replay时先找checkpoint,先从checkpoint中的数据段回放,然后是checkpoint.000003的下一个数据段000004

data
└── wal├── checkpoint.000003|   ├── 000000|   └── 000001├── 000004└── 000005
Block的持久化存储

上面我们认识了wal和chunks_head的存储构造,接下来是Block,什么是持久化Block?在什么时候创建?为啥要合并Block?

Block的目录结构

├── 01BKGTZQ1SYQJTR4PB43C8PD98   # block ID
│   ├── chunks  	   # Block中的chunk文件
│   │   └── 000001     
│   ├── tombstones     # 数据删除记录文件
│   ├── index          # 索引
│   └── meta.json	   # bolck元信息

磁盘上的Block是固定时间范围内的chunk的集合,由它自己的索引组成。其中包含多个文件的目录。每个Block都有一个唯一的 ID(ULID),他这个ID是可排序的。当我们需要更新、修改Block中的一些样本时,Prometheus TSDB只能重写整个Block,并且新块具有新的 ID(为了实现后面提到的索引)。如果需要删除的话Prometheus TSDB通过tombstones 实现了在不触及原始样本的情况下进行清理。

tombstones 可以认为是一个删除标记,它记载了我们在读取序列期间要忽略哪些时间范围。tombstones 是Block中唯一在写入数据后用于存储删除请求所创建和修改的文件。

tombstones中的记录数据结构如下,分别对应需要忽略的序列、开始和结束时间。

┌────────────────────────┬─────────────────┬─────────────────┐
│ series ref <uvarint64> │ mint <varint64> │ maxt <varint64> │
└────────────────────────┴─────────────────┴─────────────────┘

meta.json

meta.json包含了整个Block的所有元数据

{"ulid": "01EM6Q6A1YPX4G9TEB20J22B2R","minTime": 1602237600000,"maxTime": 1602244800000,"stats": {"numSamples": 553673232,"numSeries": 1346066,"numChunks": 4440437},"compaction": {"level": 1,"sources": ["01EM65SHSX4VARXBBHBF0M0FDS","01EM6GAJSYWSQQRDY782EA5ZPN"]},"version": 1
}

记录了人类可读的chunks的开始和结束时间,样本、序列、chunks数量以及合并信息。version告诉Prometheus如何解析metadata

Block合并

image-20220413113035412

我们可以从之前的图中看到当内存映射中chunk跨越2小时(默认)后第一个Block就被创建了,当 Prometheus 创建了一堆Block时,我们需要定期对这些块进行维护,以有效利用磁盘并保持查询的性能。

Block合并的主要工作是将一个或多个现有块(source blocks or parent blocks)写入一个新块,最后,删除源块并使用新的合并后的Block代替这些源块。

为什么需要对Block进行合并?

  1. 上面对tombstones介绍我们知道Prometheus在对数据的删除操作会记录在单独文件stombstone中,而数据仍保留在磁盘上。因此,当stombstone序列超过某些百分比时,需要从磁盘中删除该数据。
  2. 如果样本数据值波动非常小,相邻两个Block中的大部分数据是相同的。对这些Block做合并的话可以减少重复数据,从而节省磁盘空间。
  3. 当查询命中大于1个Block时,必须合并每个块的结果,这可能会产生一些额外的开销。
  4. 如果有重叠的Block(在时间上重叠),查询它们还要对Block之间的样本进行重复数据删除,合并这些重叠块避免了重复数据删除的需要。
  5. image-20220414120529698

如上图示例所示,我们有一组顺序的Block[1, 2, 3, 4]。数据块1,2,和3可以被合并形成的新的块是[1, 4]。或者成对压缩为[1,3]。 所有的时间序列数据仍然存在,但是现在总体的数据块更少。 这显著降低了查询成本。

Block是如何删除的?

对于源数据的删除Prometheus TSDB采用了一种简单的方式:即删除该目录下不在我们保留时间窗口的块。

如下图所示,块1可以安全地被删除,而2必须保留到完全落在边界之后

image-20220413202322093

因为Block合并的存在,意味着获取越旧的数据,数据块可能就变得越大。 因此必须得有一个合并的上限,,这样块就不会增长到跨越整个数据库。通常我们可以根据保留窗口设置百分比。

如何从大量的series中检索出数据?


在Prometheus TSDB V3引擎中使用了倒排索引,倒排索引基于它们内容的子集提供对数据项的快速查找,例如我们要找出所有带有标签app ="nginx"的序列,而无需遍历每一个序列然后再检查它是否包含该标签。

首先我们给每个序列分配一个唯一ID,查询ID的复杂度是O(1),然后给每个标签建一个倒排ID表。比如包含app ="nginx"标签的ID为1,11,111那么标签"nginx"的倒排序索引为[1,11,111],这样一来如果n是我们的序列总数,m是查询的结果大小,那么使用倒排索引的查询复杂度是O(m),也就是说查询的复杂度由m的数量决定。但是在最坏的情况下,比如我们每个序列都有一个“nginx”的标签,显然此时的复杂度变为O(n)了,如果是个别标签的话无可厚非,只能稍加等待了,但是现实并非如此。

标签被关联到数百万序列是很常见的,并且往往每次查询会检索多个标签,比如我们要查询这样一个序列app =“dev”AND app =“ops” 在最坏情况下复杂度是O(n2),接着更多标签复杂度指数增长到O(n3)、O(n4)、O(n5)… 这是不可接受的。那咋办呢?

如果我们将倒排表进行排序会怎么样?

"app=dev" -> [100,1500,20000,51166]
"app=ops" -> [2,4,8,10,50,100,20000]

他们的交集为[100,20000],要快速实现这一点,我们可以通过2个游标从列表值较小的一端率先推进,当值相等时就是可以加入到结果集合当中。这样的搜索成本显然更低,在k个倒排表搜索的复杂度为O(k*n)而非最坏情况下O(n^k)

剩下就是维护这个索引,通过维护时间线与ID、标签与倒排表的映射关系,可以保证查询的高效率。


以上我们从较浅的层面了解一下Prometheus TSDB存储相关的内容,本文仍然有很多细节没有提及,比如wal如何做压缩与回放,mmap的原理,TSDB存储文件的数据结构等等,如果你需要进一步学习可移步参考文章。通过博客阅读:iqsing.github.io


本文参考于:

Prometheus维护者Ganesh Vernekar的系列博客Prometheus TSDB

Prometheus维护者Fabian的博客文章Writing a Time Series Database from Scratch(原文已失效)

PromCon 2017: Storing 16 Bytes at Scale - Fabian Reinartz


http://chatgpt.dhexx.cn/article/PjrK3jcf.shtml

相关文章

数据库必知必会:TiDB(8)TiDB 数据库 SQL 执行流程

数据库必知必会&#xff1a;TiDB&#xff08;8&#xff09;TiDB 数据库 SQL 执行流程 数据库 SQL 执行流程DML语句读流程概述SQL的Parse与CompileSQL的Execute DML语句写流程概述执行 DDL语句流程概要执行 知识点回顾 数据库 SQL 执行流程 在TiDB中三个重要组件&#xff1a; …

时不我待,TSDB崛起正当时

近期有小伙伴问Jesse&#xff0c;为什么你们要在现在这个时点做TSDB&#xff0c;这是个好时点吗&#xff1f;我认为这是个挺好的问题&#xff0c;因为再强的个人也比不上一个团队&#xff0c;再牛的团队也需要顺势而为。我们其实一直在深度思考“Why Now”的问题&#xff0c;因…

时间序列数据库TSDB排名

DB-Engines 中时序列数据库排名 我们先来看一下DB-Engines中关于时序列数据库的排名&#xff0c;这是当前&#xff08;2016年2月的&#xff09;排名情况&#xff1a; 下面&#xff0c;我们就按照这个排名的顺序&#xff0c;简单介绍一下这些时序列数据库中的一些。下面要介绍的…

TiDB Server

目录 TiDB Server架构 Online DDL GC 缓存管理 热点小表缓存 例题 TiDB Server架构 Protocol Layer&#xff1a;负责处理客户端的连接 Parse&#xff0c;Compile&#xff1a;负责SQL语句的解析与编译&#xff0c;并生成执行计划 Executor&#xff0c;DistSQL&#xff0…

Prometheus TSDB

TSDB 概述&#xff1a; Head: 数据库的内存部分 Block: 磁盘上持久块&#xff0c;是不变的 WAL: 预写日志系统 M-map: 磁盘及内存映射 粉红色框是传入的样品&#xff0c;样品先进入Head中存留一会&#xff0c;然后到磁盘、内存映射中&#xff08;蓝色框&#xff09;。然后当内…

TiDB体系结构之TiDB Server

TiDB体系结构之TiDB Server TiDB ServerTiDB Server主要组成模块SQL语句的解析和编译行数据与KV的转化SQL读写相关模块在线DDL相关模块TiDB的垃圾回收TiDB Server的缓存 TiDB Server TiDB Server的主要作用如下&#xff1a; 处理客户端连接SQL语句的解析和编译关系型数据与KV…

TSDB助力风电监控

各位小伙伴大家好&#xff0c;本期Jesse想再来跟大家聊聊TSDB的应用场景&#xff0c;在此也感谢尹晨所著的《时序数据库在风电监控系统中的应用》一文&#xff0c;其为我们探究TSDB在风电系统中的应用提供了重要的帮助。 本文仅代表个人观点&#xff0c;如有偏颇之处&#xff…

dbt-tidb 1.2.0 尝鲜

作者&#xff1a; shiyuhang0 原文来源&#xff1a; https://tidb.net/blog/1f56ab48 本文假设你对 dbt 有一定了解。如果是第一次接触 dbt&#xff0c;建议先阅读 官方文档 或 当 TiDB 遇见 dbt 本文中的示例基于官方维护的 jaffle_shop 项目。关于此项目的细节介绍&a…

为啥用 时序数据库 TSDB

前言 其实我之前是不太了解时序数据库以及它相关的机制的&#xff0c;只是大概知晓它的用途。但因为公司的业务需求&#xff0c;我意外参与并主导了公司内部开源时序数据库influxdb的引擎改造&#xff0c;所以我也就顺理成章的成为时序数据库“从业者”。 造飞机的人需要时刻…

Prometheus 学习之——本地存储 TSDB

Prometheus 学习之——本地存储 TSDB 文章目录 Prometheus 学习之——本地存储 TSDB前言一、TSDB 核心概念二、详细介绍1.block1&#xff09;chunks2&#xff09;index3&#xff09;tombstone4&#xff09;meta.json 2.WAL 总结 前言 Prometheus 是 CNCF 收录的第二个项目&…

阿里云IoT物模型上报数据流转到实例内TSDB

阿里云物联网平台上存储的数据最多为30天&#xff0c;为了能让数据永久保存下来&#xff0c;就需要把物联网平台的数据流转到其他的数据库&#xff0c;对于企业版实例&#xff0c;内部有一个实例内的时空数据库TSDB&#xff0c;正好可以利用。下边就介绍一下物联网平台的物模型…

使用TSDB自动检测时序数据的异常情况

本期Jesse就带大家来继续了解一下TSDB的应用问题&#xff0c;小伙伴们&#xff0c;让我们直接步入正题吧。 本文仅代表个人观点&#xff0c;如有偏颇之处&#xff0c;还请海涵&#xff5e; 现今&#xff0c;每天都有数千亿个传感器产生大量时序数据。公司收集大量数据使得分析…

TSDB 存储引擎介绍

本文介绍 DolphinDB 在2.0版本中新推出的存储引擎 TSDB。 1. OLAP 与 TSDB 适用的场景 OLAP 是 DolphinDB 在2.0版本之前的唯一存储引擎。数据表中每个分区的每一列存为一个文件。数据在表中的存储顺序与数据写入的顺序一致&#xff0c;数据写入有非常高的效率。 OLAP 引擎的…

TSDB与Blockchain

各位小伙伴们我们又见面了&#xff0c;伴随着区块链技术的推广&#xff0c;很多企业也在思考是否将其应用。其实TSDB与区块链有很多共通之处&#xff0c;本期Jesse就来和大家聊聊TSDB与区块链。本文参考了Nicolas Hourcard的文章“You Don’t Need a Blockchain, You Need a Ti…

FlashDB嵌入式数据库之TSDB数据存储解析

一、驱动层&#xff1a;SFUD&#xff08;Serial Flash Universal Driver&#xff09; 是一款开源的串行 SPI Flash 通用驱动库 二、中间层&#xff1a;FAL&#xff08;FLASH ABSTRACTION LAYER)&#xff09;FLASH 抽象层 三、应用层&#xff1a;FlashDB&#xff08;FlashDB 是一…

时间序列数据库 (TSDB)

参考文档&#xff1a;时间序列数据库 TSDB_时间序列数据库 TSDB-阿里云帮助中心 什么是时序数据库 时序数据是随时间不断产生的一系列数据&#xff0c;简单来说&#xff0c;就是带时间戳的数据。数据可能来自服务器和应用程序的指标、物联网传感器的读数、网站或应用程序上的…

TSDB在高速公路大数据平台的应用

好久没有跟大家聊TSDB的应用场景了&#xff0c;Jesse也在国庆期间进行了补课&#xff0c;今天就跟大家聊聊TSDB在高速公路大数据平台的应用。本文借鉴了郝建明、袁逸涛发表在《上海船舶运输科学研究生学报》的《基于时序数据库的高速公路数据集成平台》一文&#xff0c;感谢二位…

时序数据库(TSDB)

时序数据库&#xff08;TSDB&#xff09;是一种特定类型的数据库&#xff0c;主要用来存储时序数据。随着5G技术的不断成熟&#xff0c;物联网技术将会使得万物互联。物联网时代之前只有手机、电脑可以联网&#xff0c;以后所有设备都会联网&#xff0c;这些设备每时每刻都会吐…

TSDB数据库

目录 为什么需要时序数据库&#xff1a; 时间序列数据库的特点&#xff1a; 常见的时间序列数据库&#xff1a; 时间序列数据库存储&#xff1a; 时间序列数据库问题&#xff1a; 参考资料&#xff1a; 内容是在我球的docs上直接复制过来的&#xff0c;懒得写两份&#x…

关于时许数据库的相关名词解释

1 时序数据库TSDB 英文全称为 Time Series Database&#xff0c;提供高效存取时序数据和统计分析功能的数据管理系统。 2 时序数据&#xff08;Time Series Data&#xff09; 基于稳定频率持续产生的一系列指标监测数据。例如&#xff0c;监测某城市的空气质量时&#xff0c…