加入收藏 | 设为首页 | 会员中心 | 我要投稿 威海站长网 (https://www.0631zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

从零写一个时间序列数据库

发布时间:2019-06-12 18:32:14 所属栏目:MySql教程 来源:Fabian Reinartz
导读:编者按:Prometheus 是 CNCF 旗下的开源监控告警解决方案,它已经成为 Kubernetes 生态圈中的核心监控系统。本文作者Fabian Reinartz 是Prometheus 的核心开发者,这篇文章是其于 2017 年写的一篇关于Prometheus 中的时间序列数据库的设计思考,虽然写作时

将数百万个小文件合并为少数几个大文件使得我们用很小的开销就能保持所有的文件都打开。这就解除了对 mmap(2) 的使用的阻碍,这是一个允许我们通过文件透明地回传虚拟内存的系统调用。简单起见,你可以将其视为交换空间swap space,只是我们所有的数据已经保存在了磁盘上,并且当数据换出内存后不再会发生写入。

这意味着我们可以当作所有数据库的内容都视为在内存中却不占用任何物理内存。仅当我们访问数据库文件某些字节范围时,操作系统才会从磁盘上惰性加载lazy load页数据。这使得我们将所有数据持久化相关的内存管理都交给了操作系统。通常,操作系统更有资格作出这样的决定,因为它可以全面了解整个机器和进程。查询的数据可以相当积极的缓存进内存,但内存压力会使得页被换出。如果机器拥有未使用的内存,Prometheus 目前将会高兴地缓存整个数据库,但是一旦其他进程需要,它就会立刻返回那些内存。

因此,查询不再轻易地使我们的进程 OOM,因为查询的是更多的持久化的数据而不是装入内存中的数据。内存缓存大小变得完全自适应,并且仅当查询真正需要时数据才会被加载。

就个人理解,这就是当今大多数数据库的工作方式,如果磁盘格式允许,这是一种理想的方式,——除非有人自信能在这个过程中超越操作系统。我们做了很少的工作但确实从外面获得了很多功能。

压缩

存储系统需要定期“切”出新块并将之前完成的块写入到磁盘中。仅在块成功的持久化之后,才会被删除之前用来恢复内存块的日志文件(wal)。

我们希望将每个块的保存时间设置的相对短一些(通常配置为 2 小时),以避免内存中积累太多的数据。当查询多个块,我们必须将它们的结果合并为一个整体的结果。合并过程显然会消耗资源,一个星期的查询不应该由超过 80 个的部分结果所组成。

为了实现两者,我们引入压缩compaction。压缩描述了一个过程:取一个或更多个数据块并将其写入一个可能更大的块中。它也可以在此过程中修改现有的数据。例如,清除已经删除的数据,或重建样本块以提升查询性能。

  1. t0 t1 t2 t3 t4 now
  2. +------------+ +----------+ +-----------+ +-----------+ +-----------+
  3. | 1 | | 2 | | 3 | | 4 | | 5 mutable | before
  4. +------------+ +----------+ +-----------+ +-----------+ +-----------+
  5. +-----------------------------------------+ +-----------+ +-----------+
  6. | 1 compacted | | 4 | | 5 mutable | after (option A)
  7. +-----------------------------------------+ +-----------+ +-----------+
  8. +--------------------------+ +--------------------------+ +-----------+
  9. | 1 compacted | | 3 compacted | | 5 mutable | after (option B)
  10. +--------------------------+ +--------------------------+ +-----------+

在这个例子中我们有顺序块 [1,2,3,4]。块 1、2、3 可以压缩在一起,新的布局将会是 [1,4]。或者,将它们成对压缩为 [1,3]。所有的时间序列数据仍然存在,但现在整体上保存在更少的块中。这极大程度地缩减了查询时间的消耗,因为需要合并的部分查询结果变得更少了。

保留

我们看到了删除旧的数据在 V2 存储系统中是一个缓慢的过程,并且消耗 CPU、内存和磁盘。如何才能在我们基于块的设计上清除旧的数据?相当简单,只要删除我们配置的保留时间窗口里没有数据的块文件夹即可。在下面的例子中,块 1 可以被安全地删除,而块 2 则必须一直保留,直到它落在保留窗口边界之外。

  1. |
  2. +------------+ +----+-----+ +-----------+ +-----------+ +-----------+
  3. | 1 | | 2 | | | 3 | | 4 | | 5 | . . .
  4. +------------+ +----+-----+ +-----------+ +-----------+ +-----------+
  5. |
  6. |
  7. retention boundary

随着我们不断压缩先前压缩的块,旧数据越大,块可能变得越大。因此必须为其设置一个上限,以防数据块扩展到整个数据库而损失我们设计的最初优势。

方便的是,这一点也限制了部分存在于保留窗口内部分存在于保留窗口外的块的磁盘消耗总量。例如上面例子中的块 2。当设置了最大块尺寸为总保留窗口的 10% 后,我们保留块 2 的总开销也有了 10% 的上限。

总结一下,保留与删除从非常昂贵到了几乎没有成本。

如果你读到这里并有一些数据库的背景知识,现在你也许会问:这些都是最新的技术吗?——并不是;而且可能还会做的更好。

在内存中批量处理数据,在预写日志中跟踪,并定期写入到磁盘的模式在现在相当普遍。

我们看到的好处无论在什么领域的数据里都是适用的。遵循这一方法最著名的开源案例是 LevelDB、Cassandra、InfluxDB 和 HBase。关键是避免重复发明劣质的轮子,采用经过验证的方法,并正确地运用它们。

脱离场景添加你自己的黑魔法是一种不太可能的情况。

索引

(编辑:威海站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读