关于prometheus:浅谈时序数据库内核如何用单机扛住亿级数据写入

10次阅读

共计 3311 个字符,预计需要花费 9 分钟才能阅读完成。

本文首发于泊浮目标简书:https://www.jianshu.com/u/204…

版本 日期 备注
1.0 2021.10.19 文章首发

0. 背景

题目来源于 InfluxDB 对于它们的存储引擎诞生的背景介绍:

The workload of time series data is quite different from normal database workloads. There are a number of factors that conspire to make it very difficult to get it to scale and perform well:
- Billions of individual data points
- High write throughput
- High read throughput
- Large deletes to free up disk space
- Mostly an insert/append workload, very few updates

The first and most obvious problem is one of scale. In DevOps, for instance, you can collect hundreds of millions or billions of unique data points every day.

To prove out the numbers, let’s say we have 200 VMs or servers running, with each server collecting an average of 100 measurements every 10 seconds. Given there are 86,400 seconds in a day, a single measurement will generate 8,640 points in a day, per server. That gives us a total of 200 * 100 * 8,640 = 172,800,000 individual data points per day. We find similar or larger numbers in sensor data use cases.

最近负责了产品中一部分的监控事宜。我想到时序数据库的对于 RT 和 IOPS 的要求应该很高,因而想看看它外部是怎么实现的——会不会和我意识的 Kafka、HBase 很像。

先简略做一下科普。时序数据库是用于存储追随工夫而变动的数据,并且以工夫(工夫点或者工夫区间)来建设索引的数据库。那么它最早是利用于工业(电力行业、化工行业)利用的各类型实时监测、查看与剖析设施所采集、产生的数据,这些工业数据的典型特点是产生频率快(每一个监测点一秒钟内可产生多条数据)、重大依赖于采集工夫(每一条数据均要求对应惟一的工夫)、测点多信息量大(惯例的实时监测零碎均可达到成千上万的监测点,监测点每秒钟都在产生数据)。其数据是历史烙印,它具备不变性、唯一性、有序性。时序数据库同时具备数据结构简略、数据量大的特点。

1. 问题

用过时序数据库的同学都晓得。时序数据库的数据通常只是追加,很少删改或者基本不容许删改,查问的场景个别也是有连续性的。比方:

  • 咱们通常会在监控页面上依据察看某个工夫端的数据。在须要时,会寻找其中更细的时间段来察看。
  • 时序数据库会将告警零碎关怀的指标推送过来

1.1 Prometheus 踩过的坑

在这里,咱们先简略温习一下 Prometheus 中的数据结构。其为典型的 k - v 对,k(个别叫 Series)由 MetricNameLablesTimeStamp 组成,v 则是值。

在晚期的设计中,雷同的 Series 会依照肯定的规定组织起来,同时也会依据工夫去组织文件。于是就变成了一个矩阵:

长处是写能够并行写,读也能够并行读(无论是依据条件还是时间段)。但毛病也很显著:首先是查问会变成一个矩阵,这样的设计容易触发随机读写,这无论在 HDD(限度于转速)还是 SSD(写放大)上都很好受。

于是 Prometheus 又改良了一版存储。每一个 Series 一个文件,每个 Series 的数据在内存里存满 1KB 往下刷一次。

这样缓解了随机读写的问题,但也带来新的问题:

  1. 在数据没达到 1KB 还在内存里时,如果机器 carsh 了,那么数据则失落
  2. Series 很容易变成特地多,这会导致内存占用居高不下
  3. 持续下面的,当这些数据一口气被刷下去时,磁盘会变得很忙碌
  4. 继上,很多文件会被关上,FD 会被耗费完
  5. 当利用很久没上传数据时,内存里的数据该刷不该刷?其实是没法很好的断定的

1.2 InfluxDB 踩过的坑

1.2.1 基于 LSM Tree 的 LevelDB

LSM Tree 的写性能比读性能好的多。不过 InfluxDB 提供了删除的 API,一旦删除产生时,就很麻烦——它会插入一个墓碑记录,并期待一个查问,查问将后果集和墓碑合并,稍后合并程序则会运行,将底层数据删除。并且 InfluxDB 提供了 TTL,这意味着数据删起来是 Range 删除的。

为了防止这种较慢的删除,InfluxDB 采纳了分片设计。将不同的时间段切成不同的 LevelDB,删除时只需敞开数据库并删文件就好了。不过当数据量很大的时候,会造成文件句柄过多的问题。

1.2.2 基于 mmap B+Tree 的 BoltDB

BoltDB 基于单个文件作为数据存储,基于 mmap 的 B +Tree 在运行时的性能也并不差。但当写入数据大起来后,事件变得麻烦了起来——如何缓解一次写入数十万个 Serires 带来的问题?

为了缓解这个问题,InfluxDB 引入了 WAL,这样能够无效缓解随机写入带来的问题。将多个相邻的写入缓冲,而后一起 fresh 上来,就像 MySQL 的 BufferPool。不过这并没有解决写入吞吐量降落的问题,这个办法仅仅是迁延了这个问题的呈现。

2. 解决方案

细细想来,时序数据库的数据热点只集中在近期数据。而且多写少读、简直不删改、数据只程序追加。因而,对于时序数据库咱们则能够做出很激进的存储、拜访和保留策略(Retention Policies)。

2.1 要害数据结构

  • 参考日志构造的合并树(Log Structured Merge Tree,LSM-Tree)的变种实现代替传统关系型数据库中的 B +Tree 作为存储构造,LSM 适宜的利用场景就是 写多读少(将随机写变成程序写),且简直不删改的数据。个别实现以工夫作为 key。在 InfluxDB 中,该构造被称为 Time Structured Merge Tree。
  • 时序数据库中甚至还有一种并不常见却更加极其的模式,叫做轮替型数据库(Round Robin Database,RRD),它是以环形缓冲的思路实现,只能存储固定数量的最新数据,超期或超过容量的数据就会被轮替笼罩,因而它也有着固定的数据库容量,却能承受无限量的数据输出。

2.2 要害策略

  • WAL(Write ahead log,预写日志):和诸多数据密集型利用一样,WAL 能够保证数据的长久化,并且 缓解随机写的产生。在时序数据库中,它会被当作一个查问数据的载体——当申请产生时,存储引擎会将来自 WAL 和落盘的数据做合并。另外,它还会做基于 Snappy 的压缩,这是个耗时较小的压缩算法。
  • 设置激进的数据保留策略,比方依据过期工夫(TTL),主动删除相干数据以 节俭存储空间,同时进步查问性能。对于一般的数据库来说,数据会存储一段时间后被主动删除的这个做法,能够说是不可设想的。
  • 对数据进行再采样(Resampling)以节俭空间,比方最近几天的数据可能须要准确到秒,而查问一个月前的 冷数据 只须要准确到天,查问一年前的数据只有准确到周就够了,这样将数据从新采样汇总,能够 节俭很多存储空间

3. 小结

总体看下来,相比 Kafka、HBase 来说,时序数据库的内部结构并不简略,十分有学习价值。

3.1 参考链接

  • https://prometheus.io/docs/pr…
  • https://docs.influxdata.com/i…
  • https://blog.csdn.net/cymm_li…
  • https://zhuanlan.zhihu.com/p/…
  • https://archive.docs.influxda…
  • 周志明:《凤凰架构》
正文完
 0