Prometheus TSDB分析

概述

Prometheus是著名开源监控项目,其监控任务调度给具体的服务器,该服务器到目标上抓取监控数据,然后保存在本地的TSDB中。自定义强大的PromQL语言查询实时和历史时序数据,支持丰富的查询组合。

Prometheus 1.0版本的TSDB(V2存储引擎)基于LevelDB,并且使用了和Facebook Gorilla一样的压缩算法,能够将16个字节的数据点压缩到平均1.37个字节。

Prometheus 2.0版本引入了全新的V3存储引擎,提供了更高的写入和查询性能。本文主要分析该存储引擎设计思路。

设计思路

Prometheus将Timeseries数据按2小时一个block进行存储。每个block由一个目录组成,该目录里包含:一个或者多个chunk文件(保存timeseries数据)、一个metadata文件、一个index文件(通过metric name和labels查找timeseries数据在chunk文件的位置)。最新写入的数据保存在内存block中,达到2小时后写入磁盘。为了防止程序崩溃导致数据丢失,实现了WAL(write-ahead-log)机制,将timeseries原始数据追加写入log中进行持久化。删除timeseries时,删除条目会记录在独立的tombstone文件中,而不是立即从chunk文件删除。

这些2小时的block会在后台压缩成更大的block,数据压缩合并成更高level的block文件后删除低level的block文件。这个和leveldb、rocksdb等LSM树的思路一致。

这些设计和Gorilla的设计高度相似,所以Prometheus几乎就是等于一个缓存TSDB。它本地存储的特点决定了它不能用于long-term数据存储,只能用于短期窗口的timeseries数据保存和查询,并且不具有高可用性(宕机会导致历史数据无法读取)。

Prometheus本地存储的局限性,所以它提供了API接口用于和long-term存储集成,将数据保存到远程TSDB上。该API接口使用自定义的protocol buffer over HTTP并且并不稳定,后续考虑切换为gRPC。

磁盘文件结构

内存中的block

内存中的block数据未刷盘时,block目录下面主要保存wal文件。

./data/01BKGV7JBM69T2G1BGBGM6KB12
./data/01BKGV7JBM69T2G1BGBGM6KB12/meta.json
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000002
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000001

持久化的block

持久化的block目录下wal文件被删除,timeseries数据保存在chunk文件里。index用于索引timeseries在wal文件里的位置。

./data/01BKGV7JC0RY8A6MACW02A2PJD
./data/01BKGV7JC0RY8A6MACW02A2PJD/meta.json
./data/01BKGV7JC0RY8A6MACW02A2PJD/index
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks/000001
./data/01BKGV7JC0RY8A6MACW02A2PJD/tombstones

mmap

使用mmap读取压缩合并后的大文件(不占用太多句柄),建立进程虚拟地址和文件偏移的映射关系,只有在查询读取对应的位置时才将数据真正读到物理内存。绕过文件系统page cache,减少了一次数据拷贝。查询结束后,对应内存由Linux系统根据内存压力情况自动进行回收,在回收之前可用于下一次查询命中。因此使用mmap自动管理查询所需的的内存缓存,具有管理简单,处理高效的优势。

从这里也可以看出,它并不是完全基于内存的TSDB,和Gorilla的区别在于查询历史数据需要读取磁盘文件。

Compaction

Compaction主要操作包括合并block、删除过期数据、重构chunk数据。其中合并多个block成为更大的block,可以有效减少block个数,当查询覆盖的时间范围较长时,避免需要合并很多block的查询结果。

为提高删除效率,删除时序数据时,会记录删除的位置,只有block所有数据都需要删除时,才将block整个目录删除。因此block合并的大小也需要进行限制,避免保留了过多已删除空间(额外的空间占用)。比较好的方法是根据数据保留时长,按百分比(如10%)计算block的最大时长。

Inverted Index

Inverted Index(倒排索引)基于其内容的子集提供数据项的快速查找。简而言之,我可以查看所有标签为app=“nginx”的数据,而不必遍历每一个timeseries,并检查是否包含该标签。

为此,每个时间序列key被分配一个唯一的ID,通过它可以在恒定的时间内检索,在这种情况下,ID就是正向索引。

举个栗子:如ID为9,10,29的series包含label app="nginx",则lable "nginx"的倒排索引为[9,10,29]用于快速查询包含该label的series。

性能

在文章Writing a Time Series Database from Scratch里,作者给出了benchmark测试结果为Macbook Pro上写入达到2000万每秒。这个数据比Gorilla论文中的目标7亿次写入每分钟(1000千多万每秒)提供了更高的单机性能。

参考

Writing a Time Series Database from Scratch

Prometheus官方介绍

时间序列数据的存储和计算 - 开源时序数据库解析(四)

Prometheus TSDB分析的更多相关文章

  1. Prometheus TSDB存储原理

    Prometheus 包含一个存储在本地磁盘的时间序列数据库,同时也支持与远程存储系统集成,比如grafana cloud 提供的免费云存储API,只需将remote_write接口信息填写在Prom ...

  2. 使用loki+ mtail + grafana + prometheus server分析应用问题

    loki 是一个方便的类似prometheus 的log 系统,mtail 是一个方便的日志提取工具, 可以暴露为http 服务——支持导出prometheus metrics 环境准备 docker ...

  3. 微服务监控之三:Prometheus + Grafana Spring Boot 应用可视化监控

    一.Springboot增加Prometheus 1.Spring Boot 应用暴露监控指标,添加如下依赖 <dependency> <groupId>org.springf ...

  4. Prometheus中使用的告警规则

    参考网站:https://awesome-prometheus-alerts.grep.to/rules 这个网站上有好多常用软件的告警规则,但是有些并不一定实用,有些使用起来会有错误,这里就把这些都 ...

  5. Prometheus自身的监控告警规则

    1.先在 Prometheus 主程序目录下创建rules目录,然后在该目录下创建 prometheus-test.yml文件,内容如下: 内容很多,可以根据实际情况进行调整. 规则参考网址:http ...

  6. kubernetes 1.15.1 高可用部署 -- 从零开始

    这是一本书!!! 一本写我在容器生态圈的所学!!! 重点先知: 1. centos 7.6安装优化 2. k8s 1.15.1 高可用部署 3. 网络插件calico 4. dashboard 插件 ...

  7. PromQL全解析

    PromQL(Prometheus Query Language)为Prometheus tsdb的查询语言.是结合grafana进行数据展示和告警规则的配置的关键部分. 本文默认您已了解Promet ...

  8. 使用 Ansible 快速部署 HBase 集群

    背景 出于数据安全的考虑,自研了一个低成本的时序数据存储系统,用于存储历史行情数据. 系统借鉴了 InfluxDB 的列存与压缩策略,并基于 HBase 实现了海量存储能力. 由于运维同事缺乏 Had ...

  9. Grafana Mimir:支持乱序的指标采集

    Grafana Mimir:支持乱序的指标采集 译自:New in Grafana Mimir: Introducing out-of-order sample ingestion 很早之前在使用th ...

随机推荐

  1. Markdown使用心得

    1. 标题的使用 在使用标题时,如果为了层次清晰,可以在"#"后加上"1. "或者"1.1. "这种序号. 每一级标题的正文结束后,最好加一 ...

  2. 「CF622F」The Sum of the k-th Powers「拉格朗日插值」

    题意 求\(\sum_{i=1}^n i^k\),\(n \leq 10^9,k \leq 10^6\) 题解 观察可得答案是一个\(k+1\)次多项式,我们找\(k+2\)个值带进去然后拉格朗日插值 ...

  3. 加密模块(md5)

    一.md5加密 import hashlib s = ' print(s.encode()) m = hashlib.md5(s.encode())# 必须得传一个bytes类型的 print(m.h ...

  4. 泛型1(一些algorithm函数)

    泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作.因此算法可能改变容器中保存的元素,也可能在容器内移动元素,但永远不会直接添加或删除元素. 只读算法: accumulate: ...

  5. 洛谷P4458 /loj#2512.[BJOI2018]链上二次求和(线段树)

    题面 传送门(loj) 传送门(洛谷) 题解 我果然是人傻常数大的典型啊-- 题解在这儿 //minamoto #include<bits/stdc++.h> #define R regi ...

  6. bzoj1070【SCOI2007】修车(费用流)

    题目描述 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待 ...

  7. Python3之json模块

    概念: 序列化(Serialization):将对象的状态信息转换为可以存储或可以通过网络传输的过程,传输的格式可以是JSON,XML等.反序列化就是从存储区域(JSON,XML)读取反序列化对象的状 ...

  8. 条目四《用empty来代替检查size()是否为0》

    条目四<用empty来代替检查size()是否为0> 首先先说结论: empty()实现为内联函数.(众所周知, 优秀的内联函数的效率比一般函数是高的) 在stl标准库中,empty()对 ...

  9. jQuery EasyUI Datagrid组件默认视图分析

    在Datagrid基础DOM结构的一文中,我对Datagrid组件的骨架做了很详细的描述.有了骨架还并不完整,还得有血有肉有衣服穿才行.强大的Datagrid组件允许我们自己定义如何在基础骨架上长出健 ...

  10. SpeechVoiceSpeakFlags枚举类型的详细解释

    http://blog.csdn.net/zhou_xw6511/article/details/8313528