精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Prometheus存儲引擎簡析

jf_ro2CN3Fa ? 來源:Keep Coding ? 2023-03-28 17:57 ? 次閱讀

背景知識

時序特點

時序數據的特點可以用一話概括:垂直寫(最新數據),水平查。

6228fc52-c9f4-11ed-bfe3-dac502259ad0.jpg

對于云原生場景來說,另一個特點是數據生命周期短,一次容器的擴縮容會導致時間線膨脹一倍。了解這兩個特點后,來看看 Prometheus 是如何存儲數據來迎合上述模式:

├──01BKGV7JC0RY8A6MACW02A2PJD//block的ULID
│├──chunks
││└──000001
│├──tombstones
│├──index
│└──meta.json
├──chunks_head
│└──000001
└──wal
├──000000002
└──checkpoint.00000001
└──00000000

可以看到,數據目錄主要有以下幾部分:

block,一個時間段內(默認 2 小時)的所有數據,只讀,用 ULID 命名。每一個 block 內主要包括:

chunks 固定大?。ㄗ畲?128M)的 chunks 文件

index 索引文件,主要包含倒排索引的信息

meta.json 元信息,主要包括 block 的 minTime/maxTime,方便查詢時過濾

chunks_head,當前在寫入的 block 對應的 chunks 文件,只讀,最多 120 個數據點,時間跨度最大 2 小時。

wal,Prometheus 采用攢批的方式來異步刷盤,因此需要 WAL 來保證數據可靠性

623e4ac6-c9f4-11ed-bfe3-dac502259ad0.jpg

通過上面的目錄結構,不難看出 Prometheus 的設計思路:

通過數據按時間分片的方式來解決數據生命周期短的問題

通過內存攢批的方式來對應只寫最新數據的場景

數據模式

Prometheus 支持的模式比較簡單,只支持單值模式,如下:

cpu_usage{core="1",ip="130.25.175.171"}14.041618137750
metriclabelsvaluetimesample

倒排索引

索引是支持多維搜索的主要手段,時序中的索引結構和搜索引擎的類似,是個倒排索引,可參考下圖

624de6ac-c9f4-11ed-bfe3-dac502259ad0.jpg

在一次查詢中,會對涉及到的 label 分別求對應的 postings lists(即時間線集合),然后根據 filter 類型進行集合運算,最后根據運算結果得出的時間線,去查相應數據即可。

磁盤存儲格式

數據格式

┌──────────────────────────────┐
│magic(0x0130BC91)<4?byte>│
├──────────────────────────────┤
│version(1)<1?byte>│
├──────────────────────────────┤
│padding(0)<3?byte>│
├──────────────────────────────┤
│┌──────────────────────────┐│
││Chunk1││
│├──────────────────────────┤│
││...││
│├──────────────────────────┤│
││ChunkN││
│└──────────────────────────┘│
└──────────────────────────────┘


>基于SpringCloudAlibaba+Gateway+Nacos+RocketMQ+Vue&Element實現的后臺管理系統+用戶小程序,支持RBAC動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
>
>*項目地址:
>*視頻教程

#單個chunk內的結構
┌─────────────────────┬───────────────────────┬───────────────────────┬───────────────────┬───────────────┬──────────────┬────────────────┐
|seriesref<8?byte>|mint<8?byte,?uint64>|maxt<8?byte,?uint64>|encoding<1?byte>|len|data│CRC32<4?byte>│
└─────────────────────┴───────────────────────┴───────────────────────┴────────

chunk 為數據在磁盤中的最小組織單元,需要明確以下兩點:

單個 chunk 的時間跨度默認是 2 小時,Prometheus 后臺會有合并操作,把時間相鄰的 block 合到一起

series ref 為時間線的唯一標示,由 8 個字節組成,前 4 個表示文件 id,后 4 個表示在文件內的 offset,需配合后文的索引結構來實現數據的定位

索引格式

┌────────────────────────────┬─────────────────────┐
│magic(0xBAAAD700)<4b>│version(1)<1?byte>│
├────────────────────────────┴─────────────────────┤
│┌──────────────────────────────────────────────┐│
││SymbolTable││
│├──────────────────────────────────────────────┤│
││Series││
│├──────────────────────────────────────────────┤│
││LabelIndex1││
│├──────────────────────────────────────────────┤│
││...││
│├──────────────────────────────────────────────┤│
││LabelIndexN││
│├──────────────────────────────────────────────┤│
││Postings1││
│├──────────────────────────────────────────────┤│
││...││
│├──────────────────────────────────────────────┤│
││PostingsN││
│├──────────────────────────────────────────────┤│
││LabelOffsetTable││
│├──────────────────────────────────────────────┤│
││PostingsOffsetTable││
│├──────────────────────────────────────────────┤│
││TOC││
│└──────────────────────────────────────────────┘│
└──────────────────────────────────────────────────┘

在一個索引文件中,最主要的是以下幾部分(從下往上):

TOC 存儲的是其他部分的 offset

Postings Offset Table,用來存儲倒排索引,Key 為 label name/value 序對,Value 為 Postings 在文件中的 offset。

Postings N,存儲的是具體的時間線序列

Series,存儲的是當前時間線,對應的 chunk 文件信息

Label Offset Table 與 Label Index 目前在查詢時沒有使用到,這里不再講述

每個部分的具體編碼格式,可參考官方文檔 Index Disk Format,這里重點講述一次查詢是如何找到符合條件的數據的:

首先在 Posting Offset Table 中,找到對應 label 的 Postings 位置

625efc62-c9f4-11ed-bfe3-dac502259ad0.jpg

然后再根據 Postings 中的 series 信息,找到對應的 chunk 位置,即上文中的 series ref。

6276edb8-c9f4-11ed-bfe3-dac502259ad0.png

使用方式

Prometheus 在啟動時,會去加載數據元信息到內存中。主要有下面兩部分:

block 的元信息,最主要的是 mint/maxt,用來確定一次查詢是否需要查看當前 block 文件,之后把 chunks 文件以 mmap 方式打開

//openallblocks
bDirs,err:=blockDirs(dir)
for_,bDir:=rangebDirs{
meta,_,err:=readMetaFile(bDir)
//Seeifwealreadyhavetheblockinmemoryoropenitotherwise.
block,open:=getBlock(loaded,meta.ULID)
if!open{
block,err=OpenBlock(l,bDir,chunkPool)
iferr!=nil{
corrupted[meta.ULID]=err
continue
}
}
blocks=append(blocks,block)
}
//openchunkfiles
for_,fn:=rangefiles{
f,err:=fileutil.OpenMmapFile(fn)
iferr!=nil{
returnnil,tsdb_errors.NewMulti(
errors.Wrap(err,"mmapfiles"),
tsdb_errors.CloseAll(cs),
).Err()
}
cs=append(cs,f)
bs=append(bs,realByteSlice(f.Bytes()))
}

block 對應的索引信息,主要是倒排索引。由于單個 label 對應的 Postings 可能會非常大,Prometheus 不是全量加載,而是每隔 32 個加載,來減輕內存壓力。并且保證第一個與最后一個一定被加載,查詢時采用類似跳表的方式進行 posting 定位。

下面代碼為 DB 啟動時,讀入 postings 的邏輯:

//Forthepostingsoffsettablewekeepeverylabelnamebutonlyeverynth
//labelvalue(plusthefirstandlastone),tosavememory.
ReadOffsetTable(r.b,r.toc.PostingsTable,func(key[]string,_uint64,offint)error{
if_,ok:=r.postings[key[0]];!ok{
//Nextlabelname.
r.postings[key[0]]=[]postingOffset{}
iflastKey!=nil{
//Alwaysincludelastvalueforeachlabelname.
r.postings[lastKey[0]]=append(r.postings[lastKey[0]],postingOffset{value:lastKey[1],off:lastOff})
}
lastKey=nil
valueCount=0
}
ifvalueCount%32==0{
r.postings[key[0]]=append(r.postings[key[0]],postingOffset{value:key[1],off:off})
lastKey=nil
}else{
lastKey=key
lastOff=off
}
valueCount++
}
iflastKey!=nil{
r.postings[lastKey[0]]=append(r.postings[lastKey[0]],postingOffset{value:lastKey[1],off:lastOff})
}

下面代碼為根據 label 查詢 postings 的邏輯,完整可見 index 的 Postings 方法:

e,ok:=r.postings[name]//name為labelkey
if!ok||len(values)==0{//values為當前需要查詢的labelvalues
returnEmptyPostings(),nil
}
res:=make([]Postings,0,len(values))
skip:=0
valueIndex:=0
forvalueIndex=value})
ifi==len(e){
//We'repasttheend.
break
}
ifi>0&&e[i].value!=value{//postings中沒有該value,需要用前面一個來在文件中搜索
//Needtolookfrompreviousentry.
i--
}
//Don'tCrc32theentirepostingsoffsettable,thisisveryslow
//sohopeanyissueswerecaughtatstartup.
d:=encoding.NewDecbufAt(r.b,int(r.toc.PostingsTable),nil)
d.Skip(e[i].off)
//Iterateontheoffsettable.
varpostingsOffuint64//Theoffsetintothepostingstable.
ford.Err()==nil{
//...skip邏輯省略
v:=d.UvarintBytes()//Labelvalue.
postingsOff=d.Uvarint64()//Offset.
forstring(v)>=value{
ifstring(v)==value{
//Readfromthepostingstable.
d2:=encoding.NewDecbufAt(r.b,int(postingsOff),castagnoliTable)
_,p,err:=r.dec.Postings(d2.Get())
res=append(res,p)
}
valueIndex++
ifvalueIndex==len(values){
break
}
value=values[valueIndex]
}
ifi+1==len(e)||value>=e[i+1].value||valueIndex==len(values){
//Needtogotoalaterpostingsoffsetentry,ifthereisone.
break
}
}
}

內存結構

Block 在 Prometheus 實現中,主要分為兩類:

當前正在寫入的,稱為 head。當超過 2 小時或超過 120 個點時,head 會將 chunk 寫入到本地磁盤中,并使用 mmap 映射到內存中,保存在下文的 mmappedChunk 中。

歷史只讀的,存放在一數組中

typeDBstruct{
blocks[]*Block
head*Head
//...忽略其他字段
}
//Block內的主要字段是IndexReader,其內部主要是postings,即倒排索引
//MapofLabelNametoalistofsomeLabelValues'spositionintheoffsettable.
//Thefirstandlastvaluesforeachnamearealwayspresent.
postingsmap[string][]postingOffset
typepostingOffsetstruct{
valuestring//labelvalue
offint//posting在對于文件中的offset
}

在上文磁盤結構中介紹過,postingOffset 不是全量加載,而是每隔 32 個。

Head

typeHeadstruct{
postings*index.MemPostings//Postingslistsforterms.
//AllseriesaddressablebytheirIDorhash.
series*stripeSeries
//...忽略其他字段
}
typeMemPostingsstruct{
mtxsync.RWMutex
mmap[string]map[string][]uint64//labelkey->labelvalue->postinglists
orderedbool
}

MemPostings 是 Head 中的索引結構,與 Block 的 postingOffset 不同,posting 是全量加載的,畢竟 Head 保存的數據較小,對內存壓力也小。

typestripeSeriesstruct{
sizeint
series[]map[uint64]*memSeries
hashes[]seriesHashmap
locks[]stripeLock
seriesLifecycleCallbackSeriesLifecycleCallback
}
typememSeriesstruct{
sync.RWMutex
mmappedChunks[]*mmappedChunk//只讀
headChunk*memChunk//讀寫
......//省略其他字段
}
typemmappedChunkstruct{
//數據文件在磁盤上的位置,即上文中的seriesref
refuint64
numSamplesuint16
minTime,maxTimeint64
}

stripeSeries 是比較的核心結構,series 字段的 key 為時間線,采用自增方式生成;value 為 memSeries,內部有存儲具體數據的 chunk,采用分段鎖思路來減少鎖競爭。

使用方式

對于一個查詢,大概涉及的步驟:

根據 label 查出所涉及到的時間線,然后根據 filter 類型,進行集合運算,找出符合要求的時間線

根據時間線信息與時間范圍信息,去 block 內查詢符合條件的數據

在第一步主要在 PostingsForMatchers 函數中完成,主要有下面幾個優化點:

對于取反的 filter( != !~ ),轉化為等于的形式,這樣因為等于形式對應的時間線往往會少于取反的效果,最后在合并時,減去這些取反的時間線即可??蓞⒖迹築e smarter in how we look at matchers. #572

不同 label 的時間線合并時,利用了時間線有序的特點,采用類似 mergesort 的方式來惰性合并,大致過程如下:

typeintersectPostingsstruct{
arr[]Postings//需要合并的時間線數組
curuint64//當前的時間線
}

func(it*intersectPostings)doNext()bool{
Loop:
for{
for_,p:=rangeit.arr{
if!p.Seek(it.cur){
returnfalse
}
ifp.At()>it.cur{
it.cur=p.At()
continueLoop
}
}
returntrue
}
}

func(it*intersectPostings)Next()bool{
for_,p:=rangeit.arr{
if!p.Next(){
returnfalse
}
ifp.At()>it.cur{
it.cur=p.At()
}
}
returnit.doNext()
}

在第一步查出符合條件的 chunk 所在文件以及 offset 信息之后,第二步的取數據則相對簡單,直接使用 mmap 讀數據即可,這間接利用操作系統的 page cache 來做緩存,自身不需要再去實現 Buffer Pool 之類的數據結構。

總結

通過上文的分析,大體上把 Prometheus 的存儲結構以及查詢流程分析了一遍,還有些細節沒再展開去介紹,比如為了節約內存使用,label 使用了字典壓縮,但這并不妨礙讀者理解其原理。

此外,Prometheus 默認 2 小時一個 Block 對大時間范圍查詢不友好,因此其后臺會對定期 chunk 文件進行 compaction,合并后的文件大小為 min(31d, retention_time * 0.1) ,相關細節后面有機會再單獨介紹吧。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 操作系統
    +關注

    關注

    37

    文章

    6545

    瀏覽量

    122731
  • Cache
    +關注

    關注

    0

    文章

    128

    瀏覽量

    28188
  • toc
    toc
    +關注

    關注

    0

    文章

    33

    瀏覽量

    8087

原文標題:Prometheus 存儲引擎分析

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    新能源電池產業鏈及投資機會-磷酸亞鐵鋰

    新能源電池產業鏈及投資機會-磷酸亞鐵鋰  一、前言
    發表于 12-25 09:34 ?962次閱讀

    Prometheus的基本原理與開發指南

    PromQL高級實戰 告警引擎深度解析 本地存儲與遠程存儲 梯度運維管理平臺監控模塊架構 01監控系統概述 導讀:本章從監控的作用、架構分類、指標監控發展史、指標監控典型架構等4個方面介紹監控系統的相關概念。 1.1.監控的作
    的頭像 發表于 11-09 10:45 ?928次閱讀
    <b class='flag-5'>Prometheus</b>的基本原理與開發指南

    prometheus做監控服務的整個流程介紹

    ;然后介紹如何收集監控數據,如何展示監控數據,如何觸發告警;最后展示一個業務系統監控的demo。監控架構Prometheus的整個架構流程可以參考如下圖片:整個流程大致分為收集數據,存儲數據,展示監控
    發表于 12-23 17:34

    MySQL存儲引擎

    MySQL存儲引擎InnoDB??InnoDB 的存儲文件有兩個,后綴名分別是.frm和.idb,其中.frm是表的定義文件,而.idb是數據文件。InnoDB 中存在表鎖和行鎖,不過行鎖是在命中
    發表于 09-06 06:07

    基于ATM理念的UTRAN傳輸架構

    基于ATM理念的UTRAN傳輸架構:UTRAN(UMTS無線接入網)系統傳輸網承載其內部業務傳送及至CN(核心網)側的業務匯聚功能,考慮3G網絡內,話音、媒體流及Internet等數據業務的多樣
    發表于 10-22 10:49 ?15次下載

    電動汽車用鋰離子電池技術的國內外進展

    電動汽車用鋰離子電池技術的國內外進展
    發表于 11-10 13:53 ?765次閱讀

    PCB線路板電鍍銅工藝

    PCB線路板電鍍銅工藝   一.電鍍工藝的分類:   酸性光亮銅電鍍電鍍鎳/金電鍍錫   二.工藝流程:
    發表于 11-17 14:01 ?3946次閱讀

    EPON技術

    EPON技術 EPON是一個新技術,用于保證提供一個高品質與高帶寬利用率的應用。   EPON在日本、韓國、中國大陸、中國臺灣及其它以以太網絡為基礎的地區都
    發表于 01-22 10:43 ?822次閱讀

    BGA封裝技術與質量控制

    BGA封裝技術與質量控制  ?。樱停裕⊿urface Mount Technology)表面安裝技術順應了電子產品小型化、輕型化的潮流趨勢,為實現電子
    發表于 03-30 16:49 ?1431次閱讀

    鼠標HID例程(中)

    鼠標 HID 例程 緊接《鼠標 HID 例程(上)》一文,繼續向大家介紹鼠 標 HID 例程的未完的內容。
    發表于 07-26 15:18 ?0次下載

    籠型三相異步電動機噪聲故障

    籠型三相異步電動機噪聲故障_陳金剛
    發表于 01-01 15:44 ?1次下載

    prometheus-book Prometheus操作指南

    ./oschina_soft/prometheus-book.zip
    發表于 05-16 09:11 ?5次下載
    <b class='flag-5'>prometheus</b>-book <b class='flag-5'>Prometheus</b>操作指南

    5G AAU 功放控制和監測模塊

    5G AAU 功放控制和監測模塊
    發表于 10-28 12:00 ?2次下載
    5G AAU 功放控制和監測模塊<b class='flag-5'>簡</b><b class='flag-5'>析</b>

    prometheus下載安裝教程

    Server 并不直接服務監控特定的目標,其主要任務負責數據的收集,存儲并且對外提供數據查詢支持。因此為了能夠能夠監控到某些東西,如主機的CPU使用率,我們需要使用到Exporter。Prometheus
    的頭像 發表于 01-13 16:07 ?7687次閱讀
    <b class='flag-5'>prometheus</b>下載安裝教程

    AFE8092幀同步特性

    AFE8092幀同步特性
    的頭像 發表于 08-24 13:37 ?557次閱讀
    AFE8092幀同步特性<b class='flag-5'>簡</b><b class='flag-5'>析</b>