promethues源码剖析(head block)

什么是Head block? v2.19之前,最近2hour的指标数据存储在memory。
v2.19引入Head block,最近的指标数据存储在memory,当head block满时,将数据存储到disk并通过mmap引用它。
Head block由若干个chunk组成,head chunk是memChunk,接收时序写入。
写入时序数据时,当写入head chunk和wal后,就返回写入成功。
promethues源码剖析(head block)
文章图片

什么是mmap? 普通文件的读写:

  • 文件先读入kernal space;
  • 文件内容被copy值user space;
  • 用户操作文件内容;
promethues源码剖析(head block)
文章图片

mmap方式下文件的读写:
  • 文件被map到kernal space后,用户空间就可以读写;
  • 相比普通文件读写,减少了一次系统调用和一次文件copy;
  • 在多进程以只读方式共享同一个文件的场景下,将节省大量的内存;
promethues源码剖析(head block)
文章图片

Head block的生命周期 1)初始状态
时序数据被写入head chunk和wal后,返回写入成功。
promethues源码剖析(head block)
文章图片

2)head chunk被写满
headChunk对每个series,保存最近的120个点的数据;
const samplesPerChunk = 120

若scrape interval=15s的话,headChunk会存储30min的指标数据;
head chunk被写满后,生成新的head chunk接受指标写入,如下图所示:
promethues源码剖析(head block)
文章图片

同时,原head chunk被flush到disk,并mmap引用它:
promethues源码剖析(head block)
文章图片

3)mmap的chunks满
mmap的chunks达到chunkRange(2hour)的3/2时,如下图所示:
promethues源码剖析(head block)
文章图片

mmap中chunkRange(2hour)的数据将被持久化到block,同时生成checkpoint & 清理wal日志。
promethues源码剖析(head block)
文章图片

Head block的源码分析 promethues源码剖析(head block)
文章图片

每个memSeries结构,包含一个headChunk,其保存1个series在mem中的数据:
// prometheus/tsdb/head.go // memSeries is the in-memory representation of a series. type memSeries struct { ... refuint64 lsetlabels.Labels ... headChunk*memChunk }type memChunk struct { chunkchunkenc.Chunk minTime, maxTime int64 }

向memSeries添加指标数据:
// prometheus/tsdb/head.go // append adds the sample (t, v) to the series. func (s *memSeries) append(t int64, v float64, appendID uint64, chunkDiskMapper *chunks.ChunkDiskMapper) (sampleInOrder, chunkCreated bool) { // 1个chunk最多120个sample const samplesPerChunk = 120 numSamples := c.chunk.NumSamples() // If we reach 25% of a chunk's desired sample count, set a definitive time // at which to start the next chunk. // 到1/4时,重新计算nextAt(120点以后的时间) if numSamples == samplesPerChunk/4 { s.nextAt = computeChunkEndTime(c.minTime, c.maxTime, s.nextAt) } // 到达时间,创建新的headChunk if t >= s.nextAt { c = s.cutNewHeadChunk(t, chunkDiskMapper) chunkCreated = true } // 向headChunk插入t/v数据 s.app.Append(t, v) ...... }

当达到nextAt后,写入老的headChunk数据,并新建headChunk:
// prometheus/tsdb/head.go func (s *memSeries) cutNewHeadChunk(mint int64, chunkDiskMapper *chunks.ChunkDiskMapper) *memChunk { // 写入mmap s.mmapCurrentHeadChunk(chunkDiskMapper)// 新建headChunk s.headChunk = &memChunk{ chunk:chunkenc.NewXORChunk(), minTime: mint, maxTime: math.MinInt64, } s.nextAt = rangeForTimestamp(mint, s.chunkRange) app, err := s.headChunk.chunk.Appender() s.app = app return s.headChunk }

将headChunk写入mmap:
// prometheus/tsdb/head.go func (s *memSeries) mmapCurrentHeadChunk(chunkDiskMapper *chunks.ChunkDiskMapper) { chunkRef, err := chunkDiskMapper.WriteChunk(s.ref, s.headChunk.minTime, s.headChunk.maxTime, s.headChunk.chunk) s.mmappedChunks = append(s.mmappedChunks, &mmappedChunk{ ref:chunkRef, numSamples: uint16(s.headChunk.chunk.NumSamples()), minTime:s.headChunk.minTime, maxTime:s.headChunk.maxTime, }) }// prometheus/tsdb/chunks/head_chunks.go // WriteChunk writes the chunk to the disk. func (cdm *ChunkDiskMapper) WriteChunk(seriesRef uint64, mint, maxt int64, chk chunkenc.Chunk) (chkRef uint64, err error) { .... // 写入header信息 if err := cdm.writeAndAppendToCRC32(cdm.byteBuf[:bytesWritten]); err != nil { return 0, err } // 写入chunk数据 if err := cdm.writeAndAppendToCRC32(chk.Bytes()); err != nil { return 0, err } if err := cdm.writeCRC32(); err != nil { return 0, err } // writeBufferSize=4M // 超过4M,则直接flush到disk if len(chk.Bytes())+MaxHeadChunkMetaSize >= writeBufferSize { if err := cdm.flushBuffer(); err != nil { return 0, err } }return chkRef, nil }

Head block的好处 prometheus在2.19.0的release note中提到:
promethues源码剖析(head block)
文章图片

可以看出,Head block带来的好处:
  • 减少了用户态内存的占用:
    • 之前最近2hour的chunks存在memory中;
    • 引入head block后,chunks通过mmap引用,不占用用户态内存;
  • 提升了prometheus实例重启的数据恢复速度:
    • 若没有head block,恢复时需要replay所有的wal到memory;
    • 有了head block后,恢复时仅需读入mmap chunks,然后replay没有mmap的部分wal即可;
参考: 【promethues源码剖析(head block)】1.https://ganeshvernekar.com/bl...
2.https://ganeshvernekar.com/bl...

    推荐阅读