Prometheus时序数据库磁盘中的存储结构
本文转载自解bug之路 作者alchemystarlzy
前言
之前的文章里,笔者详细描述了监控数据在Prometheus内存中的结构。而其在磁盘中的存储结构,也是非常有意思的,关于这部分内容,将在本篇文章进行阐述。
磁盘目录结构
首先我们来看Prometheus运行后,所形成的文件目录结构
在笔者自己的机器上的具体结构如下:prometheus-data |-01EY0EH5JA3ABCB0PXHAPP999D (block) |-01EY0EH5JA3QCQB0PXHAPP999D (block) |-chunks |-000001 |-000002 ..... |-000021 |-index |-meta.json |-tombstones |-wal |-chunks_head
Block
一个Block就是一个独立的小型数据库,其保存了一段时间内所有查询所用到的信息。包括标签/索引/符号表数据等等。Block的实质就是将一段时间里的内存数据组织成文件形式保存下来。
最近的Block一般是存储了2小时的数据,而较为久远的Block则会通过compactor进行合并,一个Block可能存储了若干小时的信息。值得注意的是,合并操作只是减少了索引的大小(尤其是符号表的合并),而本身数据(chunks)的大小并没有任何改变。
meta.json
我们可以通过检查meta.json来得到当前Block的一些元信息。{ "ulid":"01EY0EH5JA3QCQB0PXHAPP999D" // maxTime-minTime = 7200s => 2 h "minTime": 1611664000000 "maxTime": 1611671200000 "stats": { "numSamples": 1505855631, "numSeries": 12063563, "numChunks": 12063563 } "compaction":{ "level" : 1 "sources: [ "01EY0EH5JA3QCQB0PXHAPP999D" ] } "version":1}
其中的元信息非常清楚明了。这个Block记录了2个小时的数据。
让我们再找一个比较陈旧的Block看下它的meta.json. "ulid":"01EXTEH5JA3QCQB0PXHAPP999D", // maxTime - maxTime =>162h "minTime":1610964800000, "maxTime":1611548000000 ...... "compaction":{ "level": 5, "sources: [ 31个01EX...... ] }, "parents: [ { "ulid": 01EXTEH5JA3QCQB1PXHAPP999D ... } { "ulid": 01EXTEH6JA3QCQB1PXHAPP999D ... } { "ulid": 01EXTEH5JA31CQB1PXHAPP999D ... } ]
从中我们可以看到,该Block是由31个原始Block经历5次压缩而来。最后一次压缩的三个Block ulid记录在parents中。如下图所示:
Chunks结构
CUT文件切分
所有的Chunk文件在磁盘上都不会大于512M,对应的源码为:func (w *Writer) WriteChunks(chks ...Meta) error { ...... for i, chk := range chks { cutNewBatch := (i != 0) && (batchSize+SegmentHeaderSize > w.segmentSize) ...... if cutNewBatch { ...... } ...... }}
当写入磁盘单个文件超过512M的时候,就会自动切分一个新的文件。
一个Chunks文件包含了非常多的内存Chunk结构,如下图所示:
图中也标出了,我们是怎么寻找对应Chunk的。通过将文件名(000001,前32位)以及(offset,后32位)编码到一个int类型的refId中,使得我们可以轻松的通过这个id获取到对应的chunk数据。
chunks文件通过mmap去访问
由于chunks文件大小基本固定(最大512M),所以我们很容易的可以通过mmap去访问对应的数据。直接将对应文件的读操作交给操作系统,既省心又省力。对应代码为:func NewDirReader(dir string, pool chunkenc.Pool) (*Reader, error) { ...... for _, fn := range files { f, err := fileutil.OpenMmapFile(fn) ...... } ...... bs = append(bs, realByteSlice(f.Bytes()))}通过sgmBytes := s.bs[offset]就直接能获取对应的数据
index索引结构
前面介绍完chunk文件,我们就可以开始阐述最复杂的索引结构了。
寻址过程
索引就是为了让我们快速的找到想要的内容,为了便于理解。笔者就通过一次数据的寻址来探究Prometheus的磁盘索引结构。考虑查询一个拥有系列三个标签({__name__:http_requests}{job:api-server}{instance:0})且时间为start/end的所有序列数据
我们先从选择Block开始,遍历所有Block的meta.json,找到具体的Block
前文说了,通过Labels找数据是通过倒排索引。我们的倒排索引是保存在index文件里面的。那么怎么在这个单一文件里找到倒排索引的位置呢?这就引入了TOC(Table Of Content)
TOC(Table Of Content)
由于index文件一旦形成之后就不再会改变,所以Prometheus也依旧使用mmap来进行操作。采用mmap读取TOC非常容易:func NewTOCFromByteSlice(bs ByteSlice) (*TOC, error) { ...... // indexTOCLen = 6*8+4 = 52 b := bs.Range(bs.Len()-indexTOCLen, bs.Len()) ...... return &TOC{ Symbols: d.Be64(), Series: d.Be64(), LabelIndices: d.Be64(), LabelIndicesTable: d.Be64(), Postings: d.Be64(), PostingsTable: d.Be64(), }, nil}
Posting offset table 以及 Posting倒排索引
首先我们访问的是Posting offset table。由于倒排索引按照不同的LabelPair(key/value)会有非常多的条目。所以Posing offset table就是决定到底访问哪一条Posting索引。offset就是指的这一Posting条目在文件中的偏移。
Series
我们通过三条Postings倒排索引索引取交集得出{series1,Series2,Series3,Series4}∩{series1,Series2,Series3}∩{Series2,Series3}={Series2,Series3}
也就是要读取Series2和Serie3中的数据,而Posting中的Ref(Series2)和Ref(Series3)即为这两Series在index文件中的偏移。
Series以Delta的形式记录了chunkId以及该chunk包含的时间范围。这样就可以很容易过滤出我们需要的chunk,然后再按照chunk文件的访问,即可找到最终的原始数据。
SymbolTable
值得注意的是,为了尽量减少我们文件的大小,对于Label的Name和Value这些有限的数据,我们会按照字母序存在符号表中。由于是有序的,所以我们可以直接将符号表认为是一个
[]string切片。然后通过切片的下标去获取对应的sting。考虑如下符号表:
读取index文件时候,会将SymbolTable全部加载到内存中,并组织成symbols []string这样的切片形式,这样一个Series中的所有标签值即可通过切片下标访问得到。
Label Index以及Label Table
事实上,前面的介绍已经将一个普通数据寻址的过程全部讲完了。但是index文件中还包含label索引以及label Table,这两个是用来记录一个Label下面所有可能的值而存在的。
这样,在正则的时候就可以非常容易的找到我们需要哪些LabelPair。详情可以见前篇。
事实上,真正的Label Index比图中要复杂一点。它设计成一条LabelIndex可以表示(多个标签组合)的所有数据。不过在Prometheus代码中只会采用存储一个标签对应所有值的形式。
完整的index文件结构
这里直接给出完整的index文件结构,摘自Prometheus中index.md文档。┌────────────────────────────┬─────────────────────┐│ magic(0xBAAAD700) │ version(1) <1 byte> │├────────────────────────────┴─────────────────────┤│ ┌──────────────────────────────────────────────┐ ││ │ Symbol Table │ ││ ├──────────────────────────────────────────────┤ ││ │ Series │ ││ ├──────────────────────────────────────────────┤ ││ │ Label Index 1 │ ││ ├──────────────────────────────────────────────┤ ││ │ ... │ ││ ├──────────────────────────────────────────────┤ ││ │ Label Index N │ ││ ├──────────────────────────────────────────────┤ ││ │ Postings 1 │ ││ ├──────────────────────────────────────────────┤ ││ │ ... │ ││ ├──────────────────────────────────────────────┤ ││ │ Postings N │ ││ ├──────────────────────────────────────────────┤ ││ │ Label Index Table │ ││ ├──────────────────────────────────────────────┤ ││ │ Postings Table │ ││ ├──────────────────────────────────────────────┤ ││ │ TOC │ ││ └──────────────────────────────────────────────┘ │└──────────────────────────────────────────────────┘
tombstones
由于Prometheus Block的数据一般在写完后就不会变动。如果要删除部分数据,就只能记录一下删除数据的范围,由下一次compactor组成新block的时候删除。而记录这些信息的文件即是tomstones。
Prometheus入门书籍推荐
总结
Prometheus作为时序数据库,设计了各种文件结构来保存海量的监控数据,同时还兼顾了性能。只有彻底了解其存储结构,才能更好的指导我们应用它!
欢迎大家关注我公众号,里面有各种干货,还有大礼包相送哦!
爸爸独自带娃?妈妈陷入反省中前段时间,我想出门买菜,就让丈夫自己一个人在家带娃。而回到家后,看到家里的情况,不仅令我哭笑不得。怪不得闺蜜经常吐槽她老公带娃不靠谱,原来自己的老公也是如此。如果感兴趣的朋友,可以
大型翻车现场?宝宝的调皮总能出乎意料熊孩子上演大型翻车现场,真可谓是行云流水一气呵成啊。有宝宝的家庭都知道,很多时候熊孩子的行为举止,都是因为天性使然,宝宝小的时候就是调皮的,不只是男宝,女宝也一样,而这样调皮的结果
宝妈照顾着大宝去生二宝?辛酸又可怜每一个人都有自己奋斗的目标,你们努力的样子真的很美。可能结了婚之后有了宝宝之后才明白,每天都需要照顾宝宝的日常起居,虽然琐碎不过很多宝妈也乐在其中的感觉,但是其中的辛酸很多宝妈都不
如何让孩子健康成长?这几点警戒记中国是人口大国,很多老一辈都希望多子多,这也就是人们口中的人丁兴旺。前段时间我在网上搜到一个视频,主要讲述的是一位奶奶喜得四胞胎孙子,周边的亲朋好友,分分钱来祝贺,奶奶也特别开心。
孩子到底应不应该吃盐?专家实例解析相信很多妈妈都知道,孩子差不多到6个月的时候,就应该添加辅食,那么到底什么时候才能够在孩子的辅食添加盐呢?很多老人会觉得孩子吃盐可以增长力气,但是现代的辅食喂养中,极少提到盐的知识
爸爸独自带娃2大好处!宝妈了解后立刻省心了,你还不行动?前两天,我出门买菜,让丈夫一个人独自在家带娃,回家后看到的画面让我笑翻。平时听到不少闺蜜吐槽她们老公如何不靠谱,其实我自己的丈夫也真的是这样,他在平时总会出现一些奇思妙想,以至于让
宝宝记不住三岁之前的事,原来是这个原因大家都知道,一个人的人生有很多不同的记忆的,记忆对于人的性格和三观起到重要的作用,这也意味着记忆会影响我们对事情的判断,所以很多人都说记忆是人格构成不可缺少的一部分,今天就让我们一
顺产的时候胎儿也在努力?准妈妈你知多少?对于女性来说,怀孕是十分辛苦的。在孕早期的时候,需要受到孕吐的折磨,而在孕中期的时候,不同的女性身体会出现不同的变化,甚至有可能因此变得十分肥胖,与曼妙身材来了一个永久告别。但是很
孕期里最煎熬的是什么?孕吐可排不上号孕期里什么最难熬?孕吐还只是小意思,真正难熬的还在后边呢。我当初婚后两个月就怀孕了,当时啥也不懂,每天吐了吃吃了吐,最后吐得实在没法子,每天只能饿了吃一口,一天下来大概也就只能吃个
如何让宝宝发量足?不只是遗传现在很多年轻的妈妈,都会全职在家带娃,她们对宝宝的一切十分关注。宝宝的头发怎么样?身体的发质如何?饮食怎么样?都会不断的讨论!尤其是发量的问题,很多宝妈更是关注。为何其他的孩子头发
男孩比女孩还矮?别急,只是没到身高发育期前段时间我去接自己的宝宝放学的时候,发现他班级里很多女孩子的个头居然都比男孩子高,为此我有点担心,而老师看出了我的焦虑,就跟我说,其实现在这点差距并不代表什么,很多男孩子都是到了初
3。8元和149元的手机充电器,到底差在哪里?自从去年苹果宣布为了环保,iPhone12将不再送手机充电器后,身边自己购买非原装充电器的朋友越来越多了。毕竟单买一个20W的充电器需要149元,而充电线需要145元。一套下来,3
百年乳业,维爱佳精彩亮相CBME中国孕婴童展7月14日,第21届孕婴童行业盛典CBME在上海国家会展中心盛大开幕。维爱佳(展位号11D31,11E15)作为澳洲知名乳业品牌,以婴幼儿配方奶粉为主,并携全年龄段品类产品齐亮相展
在中国当父母为什么这么累?那是你没学会偷懒亲贝网小编了解到,很多人发现中国人带娃比外国人带娃要累很多,但实际上在这样教育环境下长大的孩子,并没有比外国的孩子优秀特别多。这是因为无微不至的照顾,看似自我感动的牺牲,不仅不会给
大美股份受邀参与2021第七届中国婴幼儿发展论坛,并担任发言嘉宾2021第七届中国婴幼儿发展论坛,于7月13日在上海隆重召开。本次论坛由中国关心下一代工作委员会指导,中国关心下一代工作委员会儿童发展研究中心主办。本届论坛以变革融合共生为主题,从
2021中国妈妈生存报告解读腾讯新闻和TOPHER在母亲节当天联合发布了2021年中国妈妈生存报告,收集了1993份妈妈样本,主要集中在一二线城市,这份报告对于了解城市的妈妈面临的状况还是一个比较好的窗口。0
德国人卢安克和吴正荣,为何在中国做傻事?他们有怎样的价值观?文枭帆(旅德生活15年)有人最近在网上发帖说见到了卢安克,他还是在起初支教的地区,他看上去老了一些,他实现了承诺,回到了大山,不再暴露自己,而和孩子们在一起。一卢安克当老师不收学费
4月25日,恢复开放!4月25日九峰山生态旅游景区恢复开放景区对办公区域公共区域设施设备已全面消毒,特别对景区密闭建筑餐饮场所等进行了严格的消毒通风。景区坚持落实员工健康打卡,做好员工健康监测和报告,掌
最新,油价再次调整。今年一箱油价已便宜80元今年的油价综合分析一路跌!跌!跌!,目前止,今年到现在已经进行了7次油价调整,共呈现了3次下跌,4次搁浅的情况,现在油价较去年油价下跌幅度超出1800元吨,
五一劳动节中的老爸老妈又是一年的五一劳动节,一个不寻常的节日在2020年更不寻常,突如其来的疫情让全国的焦点都疫情上在中国,在国外,在城市,在乡村,,,各个角落。让人们没有悲观更加团结,让人们没有匆忙更
这件常遇到小事却很害人,,,微信团队严正提醒!别帮陌生人做这事,当心被封号!新华社昨天4月27日,微信安全中心官方账号发布针对利诱用户参与虚假辅助注册行为的治理公告,针对街边常见的以小礼品诱惑,辅助进行微信注
一个老头叫老张四写了几天的老张,今天得知他去世了。所以决定停更了前几天为什么要写他的故事,是因为上个月他生病住院了,检查出肺癌,我去探望。在回来的路上想写写老张,但因为家里也有自己的事情就耽搁了,