前言
前面的文章對tsdb的使用進(jìn)行了記錄封断,那么從現(xiàn)在開始會記錄一些關(guān)于opentsdb內(nèi)部實(shí)現(xiàn)的原理胶滋,標(biāo)題寫了是源碼跟蹤替蔬,其實(shí)跟蹤源碼就是為了探清楚它是怎么實(shí)現(xiàn)的肴甸。文章中不會貼上一堆一堆的源碼蹄溉,只會對它的實(shí)現(xiàn)過程咨油,以及相關(guān)的內(nèi)部結(jié)構(gòu)進(jìn)行講述。本文的主要內(nèi)容:
- uid和tsuid的概念以及這樣設(shè)計(jì)的意義柒爵。
- /api/uid/assign役电,這個(gè)接口可以對metric,tagk,tagv申請uid,將會講述其實(shí)現(xiàn)過程棉胀。
- /api/uid/uidmeta法瑟,這個(gè)接口類似于/api/uid/tsmeta,但是該接口是對uid的meta data進(jìn)行操作唁奢,將會講述其實(shí)現(xiàn)過程瓢谢。
- /api/uid/tsmeta,這個(gè)接口可以對uid對應(yīng)的meta data進(jìn)行刪除或者更新驮瞧,將會講述其實(shí)現(xiàn)過程氓扛。
uid以及tsuid
我們知道tsdb是通過metric,tags(包括tagk和tagv),timeserious來映射到value的。而且tsdb內(nèi)部映射關(guān)系论笔,是這樣的:
- 將metric,tagk1,tagv1,tagk2,tagv2...按照規(guī)則生成對應(yīng)的metric_UID,tagk1_UID,tagv1_UID,tagk2_UID,tagv2_UID,...
- 將第一步生成的全部uid生成一個(gè)tsUID采郎,其形式如 <metric_UID><tagk1_UID><tagv1_UID>[...<tagkN_UID><tagvN_UID>]。
一個(gè)tsUID就唯一映射到一個(gè)時(shí)間序列狂魔。
uid是由數(shù)字生成蒜埋,并且官方默認(rèn)的長度3byte,且說明了metric,tagk,tagv各自最大的uid個(gè)數(shù)是16,777,215最楷。其計(jì)算方式是:
3(byte) = 24(bit)
Math.pow(2,24) = 16,777,216
uid默認(rèn)是自增的形式整份,也就是從1一直自增到16,777,215待错。
這樣設(shè)計(jì)的原因究竟是什么呢,為何平白無故耗費(fèi)一些計(jì)算力去增加這個(gè)中間層面的uid呢烈评。大兄弟不要急火俄,下面會娓娓道來。
假如我們目前我們有這個(gè)時(shí)間序列
sys.cpu.0.user host=websv01.lga.mysite.com owner=operations.
我們可以就用這個(gè)字符串直接映射到這個(gè)時(shí)間序列讲冠,可是其所占的空間為60byte瓜客。
現(xiàn)在我們將metric和tags轉(zhuǎn)化成uid,假設(shè)其分別對應(yīng)的uid如下:
000000000000000000000001 000000000000000000000001 000000000000000000000001 000000000000000000000001
進(jìn)一步將二進(jìn)制換算為16進(jìn)制竿开,得到的tsUID如下:
000001000001000001
可以看到對于這個(gè)時(shí)間時(shí)序谱仪,單個(gè)時(shí)間點(diǎn)存儲空間就可以從60byte變成18byte,假如這個(gè)時(shí)間序列時(shí)間跨度很大否彩,并且十分密集疯攒,那么能節(jié)約的存儲空間就非常可觀了列荔,也是tsdb這樣設(shè)計(jì)的原因所在敬尺。
/api/uid/assign
opentsdb是依賴于hbase的下面幾個(gè)表進(jìn)行工作的:
tsdb , tsdb-meta , tsdb-tree , tsdb-uid
這里先介紹tsdb-uid這個(gè)標(biāo),這個(gè)庫是用來持久化metric,tags--->uid肌毅,以及uid--->metirc,tags映射關(guān)系的筷转。
數(shù)據(jù)庫的結(jié)構(gòu)如下:
這更正一下:列族id和列族name下面的metric列其實(shí)應(yīng)該是metrics姑原。
rowkey為\x00是計(jì)數(shù)器悬而,前面講到uid是以自增的形式,這個(gè)計(jì)數(shù)器就是用于實(shí)現(xiàn)uid的自增锭汛,每申請一個(gè)新的metric(或者tagk,tagv)對應(yīng)的計(jì)數(shù)器就會增加1笨奠,并將增加得到數(shù)值作為uid。
可以看到metric:sys.cpu.0.user的uid為000001唤殴,表中持久化了兩者的映射關(guān)系:
sys.cpu.0.user ---> 000001
000001 ---> sys.cpu.0.user
好了般婆,現(xiàn)在我們知道表結(jié)構(gòu)了,下面我們來看一下為metric分配uid的過程朵逝,即/api/uid/assign接口的實(shí)現(xiàn)過程蔚袍,對于tags的uid分配過程也是一樣的。比較簡單配名,看一遍下面不標(biāo)準(zhǔn)的流程圖就會明白啤咽,嘿嘿。
看了上圖之后渠脉,可能會想到一個(gè)問題宇整。
則多個(gè)線程對同一個(gè)字符串申請uid,此時(shí)就必須避免對uid的重復(fù)申請芋膘,tsdb中時(shí)這樣處理的:
有一個(gè)線程安全的map鳞青,在申請之前會對name進(jìn)行記錄霸饲,申請完之后有會從中刪除這條記錄。為了避免重復(fù)申請臂拓,就必須在申請之前查詢map中是否有這個(gè)name的相關(guān)記錄厚脉,若有,則無法申請埃儿;若無器仗,則可繼續(xù)申請。
/api/uid/uidmeta
該接口可以對uid的meta data進(jìn)行增刪改童番,而meta data可以看成對uid的解釋和說明精钮。
上面說我們講到了 tsdb-uid 這張表,現(xiàn)在將其表結(jié)構(gòu)補(bǔ)充一下:
我們可以看到圖1和圖3的區(qū)別之處在于:
在name列族下面多了:metric_meta,tagk_meta,tagv_meta三個(gè)列剃斧,這三個(gè)列正是以json的格式分別存儲了metric,tagk,tagv的meta_data轨香。
meta_data的增刪改查也就是對這個(gè)三個(gè)列進(jìn)行操作,其過程也十分簡單幼东。有個(gè)地方需要提一下的是臂容,那就是當(dāng)有多個(gè)線程嘗試去修改同一個(gè)meta_data時(shí),一定需要避免發(fā)生臟讀的情況根蟹。解決的辦法是:
和采取版本號的機(jī)制類似脓杉,即Atomic Compare-And-Set (CAS) ,先從庫中查詢原始值简逮,更新的時(shí)候會比較如果庫中的值已經(jīng)不是原始值了就會更新失敗球散。
/api/uid/tsmeta
該接口的作用和 /api/uid/uidmeta 接口類似,只是這個(gè)接口是對tsUID的mete data進(jìn)行操作散庶。
tsUID的meta data并不是存儲在 tsdb-uid 表中的蕉堰,而是在tsdb-meta表中,現(xiàn)在我們來看一下 tsdb-meta 的表結(jié)構(gòu):
rowkey即是tsuid悲龟,name列族下面有ts_sct屋讶,和ts_meta兩個(gè)列:
- ts_sct:存儲該時(shí)間序列收到最新數(shù)據(jù)的時(shí)間,所以每次在對這個(gè)時(shí)間序列寫入新的data point時(shí)须教,ts_sct都會更新皿渗。
- ts_meta:以json的形式存儲tsuid對應(yīng)的meta data。
清楚了這個(gè)表結(jié)構(gòu)的之后轻腺,對meta data的增刪改查的操作就十分簡單乐疆。
在查詢中,除了返回meta data之外约计,還會將tsuid解析為metric,tags诀拭,并在 tsdb-uid 表中查詢metric,tags的meta data一并返回。
總結(jié)
關(guān)于/api/uid的內(nèi)容到此就全部結(jié)束了煤蚌,內(nèi)容上較為簡單耕挨,主要是熟悉其中的表結(jié)構(gòu)以及考慮數(shù)據(jù)的線程安全细卧。
嘿嘿嘿,以后博主畫流程圖會畫好看一點(diǎn)筒占。贪庙。。(#^.^#)