0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Prometheus存儲(chǔ)引擎簡(jiǎn)析

jf_ro2CN3Fa ? 來(lái)源:Keep Coding ? 2023-03-28 17:57 ? 次閱讀

背景知識(shí)

時(shí)序特點(diǎn)

時(shí)序數(shù)據(jù)的特點(diǎn)可以用一話概括:垂直寫(最新數(shù)據(jù)),水平查。

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

對(duì)于云原生場(chǎng)景來(lái)說(shuō),另一個(gè)特點(diǎn)是數(shù)據(jù)生命周期短,一次容器的擴(kuò)縮容會(huì)導(dǎo)致時(shí)間線膨脹一倍。了解這兩個(gè)特點(diǎn)后,來(lái)看看 Prometheus 是如何存儲(chǔ)數(shù)據(jù)來(lái)迎合上述模式:

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

可以看到,數(shù)據(jù)目錄主要有以下幾部分:

block,一個(gè)時(shí)間段內(nèi)(默認(rèn) 2 小時(shí))的所有數(shù)據(jù),只讀,用 ULID 命名。每一個(gè) block 內(nèi)主要包括:

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

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

meta.json 元信息,主要包括 block 的 minTime/maxTime,方便查詢時(shí)過(guò)濾

chunks_head,當(dāng)前在寫入的 block 對(duì)應(yīng)的 chunks 文件,只讀,最多 120 個(gè)數(shù)據(jù)點(diǎn),時(shí)間跨度最大 2 小時(shí)。

wal,Prometheus 采用攢批的方式來(lái)異步刷盤,因此需要 WAL 來(lái)保證數(shù)據(jù)可靠性

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

通過(guò)上面的目錄結(jié)構(gòu),不難看出 Prometheus 的設(shè)計(jì)思路:

通過(guò)數(shù)據(jù)按時(shí)間分片的方式來(lái)解決數(shù)據(jù)生命周期短的問(wèn)題

通過(guò)內(nèi)存攢批的方式來(lái)對(duì)應(yīng)只寫最新數(shù)據(jù)的場(chǎng)景

數(shù)據(jù)模式

Prometheus 支持的模式比較簡(jiǎn)單,只支持單值模式,如下:

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

倒排索引

索引是支持多維搜索的主要手段,時(shí)序中的索引結(jié)構(gòu)和搜索引擎的類似,是個(gè)倒排索引,可參考下圖

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

在一次查詢中,會(huì)對(duì)涉及到的 label 分別求對(duì)應(yīng)的 postings lists(即時(shí)間線集合),然后根據(jù) filter 類型進(jìn)行集合運(yùn)算,最后根據(jù)運(yùn)算結(jié)果得出的時(shí)間線,去查相應(yīng)數(shù)據(jù)即可。

磁盤存儲(chǔ)格式

數(shù)據(jù)格式

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


>基于SpringCloudAlibaba+Gateway+Nacos+RocketMQ+Vue&Element實(shí)現(xiàn)的后臺(tái)管理系統(tǒng)+用戶小程序,支持RBAC動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
>
>*項(xiàng)目地址:
>*視頻教程

#單個(gè)chunk內(nèi)的結(jié)構(gòu)
┌─────────────────────┬───────────────────────┬───────────────────────┬───────────────────┬───────────────┬──────────────┬────────────────┐
|seriesref<8?byte>|mint<8?byte,?uint64>|maxt<8?byte,?uint64>|encoding<1?byte>|len|data│CRC32<4?byte>│
└─────────────────────┴───────────────────────┴───────────────────────┴────────

chunk 為數(shù)據(jù)在磁盤中的最小組織單元,需要明確以下兩點(diǎn):

單個(gè) chunk 的時(shí)間跨度默認(rèn)是 2 小時(shí),Prometheus 后臺(tái)會(huì)有合并操作,把時(shí)間相鄰的 block 合到一起

series ref 為時(shí)間線的唯一標(biāo)示,由 8 個(gè)字節(jié)組成,前 4 個(gè)表示文件 id,后 4 個(gè)表示在文件內(nèi)的 offset,需配合后文的索引結(jié)構(gòu)來(lái)實(shí)現(xiàn)數(shù)據(jù)的定位

索引格式

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

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

TOC 存儲(chǔ)的是其他部分的 offset

Postings Offset Table,用來(lái)存儲(chǔ)倒排索引,Key 為 label name/value 序?qū)?,Value 為 Postings 在文件中的 offset。

Postings N,存儲(chǔ)的是具體的時(shí)間線序列

Series,存儲(chǔ)的是當(dāng)前時(shí)間線,對(duì)應(yīng)的 chunk 文件信息

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

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

首先在 Posting Offset Table 中,找到對(duì)應(yīng) label 的 Postings 位置

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

然后再根據(jù) Postings 中的 series 信息,找到對(duì)應(yīng)的 chunk 位置,即上文中的 series ref。

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

使用方式

Prometheus 在啟動(dòng)時(shí),會(huì)去加載數(shù)據(jù)元信息到內(nèi)存中。主要有下面兩部分:

block 的元信息,最主要的是 mint/maxt,用來(lái)確定一次查詢是否需要查看當(dāng)前 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 對(duì)應(yīng)的索引信息,主要是倒排索引。由于單個(gè) label 對(duì)應(yīng)的 Postings 可能會(huì)非常大,Prometheus 不是全量加載,而是每隔 32 個(gè)加載,來(lái)減輕內(nèi)存壓力。并且保證第一個(gè)與最后一個(gè)一定被加載,查詢時(shí)采用類似跳表的方式進(jìn)行 posting 定位。

下面代碼為 DB 啟動(dòng)時(shí),讀入 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})
}

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

e,ok:=r.postings[name]//name為labelkey
if!ok||len(values)==0{//values為當(dāng)前需要查詢的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,需要用前面一個(gè)來(lái)在文件中搜索
//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
}
}
}

內(nèi)存結(jié)構(gòu)

Block 在 Prometheus 實(shí)現(xiàn)中,主要分為兩類:

當(dāng)前正在寫入的,稱為 head。當(dāng)超過(guò) 2 小時(shí)或超過(guò) 120 個(gè)點(diǎn)時(shí),head 會(huì)將 chunk 寫入到本地磁盤中,并使用 mmap 映射到內(nèi)存中,保存在下文的 mmappedChunk 中。

歷史只讀的,存放在一數(shù)組中

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

在上文磁盤結(jié)構(gòu)中介紹過(guò),postingOffset 不是全量加載,而是每隔 32 個(gè)。

Head

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

MemPostings 是 Head 中的索引結(jié)構(gòu),與 Block 的 postingOffset 不同,posting 是全量加載的,畢竟 Head 保存的數(shù)據(jù)較小,對(duì)內(nèi)存壓力也小。

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

stripeSeries 是比較的核心結(jié)構(gòu),series 字段的 key 為時(shí)間線,采用自增方式生成;value 為 memSeries,內(nèi)部有存儲(chǔ)具體數(shù)據(jù)的 chunk,采用分段鎖思路來(lái)減少鎖競(jìng)爭(zhēng)。

使用方式

對(duì)于一個(gè)查詢,大概涉及的步驟:

根據(jù) label 查出所涉及到的時(shí)間線,然后根據(jù) filter 類型,進(jìn)行集合運(yùn)算,找出符合要求的時(shí)間線

根據(jù)時(shí)間線信息與時(shí)間范圍信息,去 block 內(nèi)查詢符合條件的數(shù)據(jù)

在第一步主要在 PostingsForMatchers 函數(shù)中完成,主要有下面幾個(gè)優(yōu)化點(diǎn):

對(duì)于取反的 filter( != !~ ),轉(zhuǎn)化為等于的形式,這樣因?yàn)榈扔谛问綄?duì)應(yīng)的時(shí)間線往往會(huì)少于取反的效果,最后在合并時(shí),減去這些取反的時(shí)間線即可??蓞⒖迹築e smarter in how we look at matchers. #572

不同 label 的時(shí)間線合并時(shí),利用了時(shí)間線有序的特點(diǎn),采用類似 mergesort 的方式來(lái)惰性合并,大致過(guò)程如下:

typeintersectPostingsstruct{
arr[]Postings//需要合并的時(shí)間線數(shù)組
curuint64//當(dāng)前的時(shí)間線
}

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 信息之后,第二步的取數(shù)據(jù)則相對(duì)簡(jiǎn)單,直接使用 mmap 讀數(shù)據(jù)即可,這間接利用操作系統(tǒng)的 page cache 來(lái)做緩存,自身不需要再去實(shí)現(xiàn) Buffer Pool 之類的數(shù)據(jù)結(jié)構(gòu)。

總結(jié)

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

此外,Prometheus 默認(rèn) 2 小時(shí)一個(gè) Block 對(duì)大時(shí)間范圍查詢不友好,因此其后臺(tái)會(huì)對(duì)定期 chunk 文件進(jìn)行 compaction,合并后的文件大小為 min(31d, retention_time * 0.1) ,相關(guān)細(xì)節(jié)后面有機(jī)會(huì)再單獨(dú)介紹吧。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6617

    瀏覽量

    123032
  • Cache
    +關(guān)注

    關(guān)注

    0

    文章

    129

    瀏覽量

    28231
  • toc
    toc
    +關(guān)注

    關(guān)注

    0

    文章

    33

    瀏覽量

    8100

原文標(biāo)題:Prometheus 存儲(chǔ)引擎分析

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    新能源電池產(chǎn)業(yè)鏈及投資機(jī)會(huì)簡(jiǎn)-磷酸亞鐵鋰

    新能源電池產(chǎn)業(yè)鏈及投資機(jī)會(huì)簡(jiǎn)-磷酸亞鐵鋰  一、前言
    發(fā)表于 12-25 09:34 ?968次閱讀

    Prometheus的基本原理與開發(fā)指南

    PromQL高級(jí)實(shí)戰(zhàn) 告警引擎深度解析 本地存儲(chǔ)與遠(yuǎn)程存儲(chǔ) 梯度運(yùn)維管理平臺(tái)監(jiān)控模塊架構(gòu) 01監(jiān)控系統(tǒng)概述 導(dǎo)讀:本章從監(jiān)控的作用、架構(gòu)分類、指標(biāo)監(jiān)控發(fā)展史、指標(biāo)監(jiān)控典型架構(gòu)等4個(gè)方面介紹監(jiān)控系統(tǒng)的相關(guān)概念。 1.1.監(jiān)控的作
    的頭像 發(fā)表于 11-09 10:45 ?1012次閱讀
    <b class='flag-5'>Prometheus</b>的基本原理與開發(fā)指南

    prometheus做監(jiān)控服務(wù)的整個(gè)流程介紹

    ;然后介紹如何收集監(jiān)控?cái)?shù)據(jù),如何展示監(jiān)控?cái)?shù)據(jù),如何觸發(fā)告警;最后展示一個(gè)業(yè)務(wù)系統(tǒng)監(jiān)控的demo。監(jiān)控架構(gòu)Prometheus的整個(gè)架構(gòu)流程可以參考如下圖片:整個(gè)流程大致分為收集數(shù)據(jù),存儲(chǔ)數(shù)據(jù),展示監(jiān)控
    發(fā)表于 12-23 17:34

    MySQL存儲(chǔ)引擎簡(jiǎn)

    MySQL存儲(chǔ)引擎InnoDB??InnoDB 的存儲(chǔ)文件有兩個(gè),后綴名分別是.frm和.idb,其中.frm是表的定義文件,而.idb是數(shù)據(jù)文件。InnoDB 中存在表鎖和行鎖,不過(guò)行鎖是在命中
    發(fā)表于 09-06 06:07

    基于ATM理念的UTRAN傳輸架構(gòu)簡(jiǎn)

    基于ATM理念的UTRAN傳輸架構(gòu)簡(jiǎn):UTRAN(UMTS無(wú)線接入網(wǎng))系統(tǒng)傳輸網(wǎng)承載其內(nèi)部業(yè)務(wù)傳送及至CN(核心網(wǎng))側(cè)的業(yè)務(wù)匯聚功能,考慮3G網(wǎng)絡(luò)內(nèi),話音、媒體流及Internet等數(shù)據(jù)業(yè)務(wù)的多樣
    發(fā)表于 10-22 10:49 ?15次下載

    電動(dòng)汽車用鋰離子電池技術(shù)的國(guó)內(nèi)外進(jìn)展簡(jiǎn)

    電動(dòng)汽車用鋰離子電池技術(shù)的國(guó)內(nèi)外進(jìn)展簡(jiǎn)
    發(fā)表于 11-10 13:53 ?776次閱讀

    PCB線路板電鍍銅工藝簡(jiǎn)

    PCB線路板電鍍銅工藝簡(jiǎn)   一.電鍍工藝的分類:   酸性光亮銅電鍍電鍍鎳/金電鍍錫   二.工藝流程:
    發(fā)表于 11-17 14:01 ?3970次閱讀

    EPON技術(shù)簡(jiǎn)

    EPON技術(shù)簡(jiǎn) EPON是一個(gè)新技術(shù),用于保證提供一個(gè)高品質(zhì)與高帶寬利用率的應(yīng)用。   EPON在日本、韓國(guó)、中國(guó)大陸、中國(guó)臺(tái)灣及其它以以太網(wǎng)絡(luò)為基礎(chǔ)的地區(qū)都
    發(fā)表于 01-22 10:43 ?840次閱讀

    簡(jiǎn)BGA封裝技術(shù)與質(zhì)量控制

    簡(jiǎn)BGA封裝技術(shù)與質(zhì)量控制  ?。樱停裕⊿urface Mount Technology)表面安裝技術(shù)順應(yīng)了電子產(chǎn)品小型化、輕型化的潮流趨勢(shì),為實(shí)現(xiàn)電子
    發(fā)表于 03-30 16:49 ?1447次閱讀

    鼠標(biāo)HID例程(中)簡(jiǎn)

    鼠標(biāo) HID 例程簡(jiǎn) 緊接《鼠標(biāo) HID 例程簡(jiǎn)(上)》一文,繼續(xù)向大家介紹鼠 標(biāo) HID 例程的未完的內(nèi)容。
    發(fā)表于 07-26 15:18 ?0次下載

    籠型三相異步電動(dòng)機(jī)噪聲故障簡(jiǎn)

    籠型三相異步電動(dòng)機(jī)噪聲故障簡(jiǎn)_陳金剛
    發(fā)表于 01-01 15:44 ?1次下載

    prometheus-book Prometheus操作指南

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

    5G AAU 功放控制和監(jiān)測(cè)模塊簡(jiǎn)

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

    prometheus下載安裝教程

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

    AFE8092幀同步特性簡(jiǎn)

    AFE8092幀同步特性簡(jiǎn)
    的頭像 發(fā)表于 08-24 13:37 ?594次閱讀
    AFE8092幀同步特性<b class='flag-5'>簡(jiǎn)</b><b class='flag-5'>析</b>