前言在上一篇文章中,笔者详细阐述了Prometheus时序数据库在内存和磁盘的存储结构。有了前面的铺垫,笔者就可以把本文的数据插入过程解释清楚了。监控数据的插入就到这里,Promtheus从各个Endpoint抓取数据的过程笔者就不展开讨论了。相反,它仅详细说明了数据如何插入Prometheus的过程。对应方法:func(a*headAppender)Add(lsetlabels.Labels,tint64,vfloat64)(uint64,error){...//如果没有lset对应的series,则创建一个。同时,将新的series放入倒置的Postingmaps,created:=a.head.getOrCreate(lset.Hash(),lset)ifcreated{//如果有新的创建,则将新的放入a.在系列中a.series=append(a.series,record.RefSeries{Ref:s.ref,Labels:lset,})}returns.ref,a.AddFast(s.ref,t,v)}我们将使用下面以调用add函数为例:app.Add(labels.FromStrings("foo","bar"),0,0)首先getOrCreate,顾名思义,不存在就创建一个。创建过程包括维护seriesHashMap/Postings(倒排索引)/LabelIndex。如下图:然后就是AddFast方法func(a*headAppender)AddFast(refuint64,tint64,vfloat64)error{//取出对应的memSeries:=a.head.series.getByID(ref)......//设置为等待commit状态s.pendingCommit=true...//对于transaction的概念,先放入tempstorage,等待realcommit后再写入memSeriesa.samples=append(a.samples,record.RefSample{Ref:ref,T:t,V:v,})//}Prometheus在添加数据点时并没有直接添加到memSeries(即query使用的结构)中,而是添加到一个临时的samples中里面切片。同时将这个数据点对应的memSeries同步添加到另一个sampleSeries中。为什么交易可见性会这样做?就是实现commit语义,commit之后数据才可见(可以查询)。否则,数据是看不到的。commit的动作主要是WAL(WriteAheadLog)以及将headerAppender.samples数据写入到其对应的memSeries中。这样查询就可以看到数据了,如下图:WAL并不能防止服务器崩溃丢失数据,因为内存中存放的是Prometheus的最新数据。它在提交之前写入日志WAL。当服务重新启动时,从WAL日志中获取信息并重播。为了性能,Prometheus使用了另一个goroutine来做文件同步操作,所以不能保证WAL不会丢失。此外,也无法保证监控数据不会丢失。这也是由监控服务的特性决定的。写入代码为:commit()|=>func(a*headAppender)log()error{......//将对应的系列信息写入WALiflen(a.series)>0{rec=enc.系列(a.series,buf)buf=rec[:0]iferr:=a.head.wal.Log(rec);err!=nil{returnerrors.Wrap(err,"logseries")}}...。..//写真实样本iflen(a.samples)>0{rec=enc.Samples(a.samples,buf)buf=rec[:0]iferr:=a.head.wal.Log(rec);err!=nil{returnerrors.Wrap(err,"logsamples")}}}对应WAL日志格式:系列记录┌────────────────────────────────────────────────────────────────┐│type=1<1b>│├────────────────────────────────────────────────────────────────┤│┌──────────────────────────────────────────────────────────────────────────────┐│││id<8b>│n=len(labels)
