目前,數據庫的(de)(de)存儲引擎(qing)可以(yi)粗(cu)略分為兩大類(lei)(lei):一類(lei)(lei)是基(ji)于(yu) B-Tree 的(de)(de),另一類(lei)(lei)是基(ji)于(yu) LSM Tree 的(de)(de)。前者常見(jian)于(yu)傳統 OLTP 數據庫,比(bi)(bi)如 MySQL、PQ 這類(lei)(lei)的(de)(de)默認引擎(qing),更適(shi)用于(yu)讀多(duo)寫(xie)(xie)(xie)少(shao)的(de)(de)場景;如 HBase、LevelDB、RocksDB 一類(lei)(lei) Database 使用的(de)(de)是 LSM Tree,在(zai)寫(xie)(xie)(xie)多(duo)讀少(shao)的(de)(de)場景下比(bi)(bi)較適(shi)合(he)。實際上(shang),現代數據庫的(de)(de)存儲引擎(qing),基(ji)本都會在(zai)某種(zhong)程(cheng)度下對這兩者融合(he)。LSM Tree 上(shang)怎么就(jiu)不可以(yi)建(jian) B-Tree Index 了?(HBase 在(zai) region 上(shang)也有 B-Tree Index)B-Tree 怎么就(jiu)一定(ding)要直寫(xie)(xie)(xie)硬盤,不能(neng)先(xian)寫(xie)(xie)(xie) WAL 和(he)走內存 Cache 呢(ni)?
對于存儲引擎,時序數據庫 Time Series Database(TSDB) 的(de)(de)先行者(zhe) InfluxDB 曾經做(zuo)過很多(duo)(duo)嘗試,在各(ge)個存儲引擎(LevelDB、RocksDB、BoltDB 等)之間反(fan)復(fu)橫跳(tiao),遇到(dao)過的(de)(de)問題也有很多(duo)(duo),比(bi)如(ru) BoltDB 中 mmap+BTree 模型中隨機 IO 導致的(de)(de)吞吐量低、RocksDB 這(zhe)類(lei)純 LSM Tree 存儲引擎沒辦法很優雅快(kuai)速地按(an)時間分區(qu)刪除、多(duo)(duo)個 LevelDB + 劃分時間分區(qu)的(de)(de)方法又(you)會產生(sheng)大(da)量句(ju)柄……踩了這(zhe)一(yi)系列的(de)(de)坑(keng)后(hou),最終 InfluxDB 換成了自研的(de)(de)存儲引擎 TSM。可見對 TSDB 來(lai)說(shuo),一(yi)個好的(de)(de)存儲引擎有多(duo)(duo)么重要(yao),又(you)是多(duo)(duo)么難得,要(yao)想做(zuo)到(dao)極致,還得自己研發。
同為時序數據庫Time Series Database,不同于 InfluxDB 的是,TDengine 從一開始就是自研的——從 LSM Tree 中汲取了 WAL、先寫內存的 skip list 等技術,但把 LSM Tree 的樹層級結構去掉了,而只是按時間段分區、按表分塊的 log 塊。
讀到(dao)這里,細心(xin)的讀者(zhe)可(ke)能(neng)會(hui)發(fa)現,按表分塊的設計和(he) OpenTSDB 的行(xing)聚合(he)(he)有(you)些相(xiang)(xiang)似。 OpenTSDB 的行(xing)聚合(he)(he)是(shi)把相(xiang)(xiang)同 tag 以(yi)一小時(shi)為時(shi)間范圍(wei),將這些數(shu)據(ju)都(dou)放到(dao)一行(xing)中存儲,這樣大大減少了聚合(he)(he)查詢要掃(sao)描的數(shu)據(ju)量。不過不同的是(shi),TDengine 是(shi)多(duo)列(lie)模型(xing),而 OpenTSDB 是(shi)單列(lie)模型(xing),單列(lie)模型(xing)下是(shi)多(duo)行(xing)的聚合(he)(he),多(duo)列(lie)模型(xing)下聚合(he)(he)會(hui)自然形成數(shu)據(ju)塊。
而(er)熟悉 LSM Tree 的(de)(de)(de)(de) KV 分(fen)離(li)設(she)計(ji)的(de)(de)(de)(de)朋(peng)友應(ying)(ying)該也能夠(gou)從 TDengine 的(de)(de)(de)(de)存儲(chu)引(yin)擎設(she)計(ji)中看到(dao)一(yi)些熟悉的(de)(de)(de)(de)影子。如果把(ba)數(shu)據塊作為 TSDB 存儲(chu)引(yin)擎的(de)(de)(de)(de) value,那么(me) key 就應(ying)(ying)該是塊的(de)(de)(de)(de)起止(zhi)時(shi)(shi)間 ,把(ba) key 提(ti)出來自(zi)然就得(de)到(dao)了 TDengine 的(de)(de)(de)(de) BRIN 索(suo)引(yin)。從這種視角(jiao)來看,TDengine 的(de)(de)(de)(de) .head 文件(jian)就是 key,而(er) .data 和 .last 文件(jian)就是 value,而(er) key 自(zi)身又(you)可(ke)以(yi)結合時(shi)(shi)間序列數(shu)據的(de)(de)(de)(de)特征(zheng)組合成(cheng)有序文件(jian)。 在(zai)時(shi)(shi)序場景下,有了 BRIN 索(suo)引(yin),也就可(ke)以(yi)不需要 bloom filter,這樣(yang)一(yi)看,TDengine 的(de)(de)(de)(de)存儲(chu)引(yin)擎設(she)計(ji)就很清晰了。
此外,TDengine 會將 tag 數據(ju)(ju)和時間序(xu)列數據(ju)(ju)分離開來,這樣就(jiu)能(neng)夠大(da)大(da)減少(shao) tag 數據(ju)(ju)占用的(de)存儲空間,在數據(ju)(ju)量大(da)的(de)情況下尤(you)其顯著。
TDengine 的(de)(de)(de) tag 與時間序列數(shu)據(ju)(ju)(ju)的(de)(de)(de)劃分(fen)(fen),和(he)數(shu)倉(cang)的(de)(de)(de)維(wei)度(du)(du)建(jian)模里面(mian)維(wei)度(du)(du)表(biao)(biao)與事(shi)實(shi)(shi)表(biao)(biao)的(de)(de)(de)劃分(fen)(fen)有些類(lei)似,tag 數(shu)據(ju)(ju)(ju)類(lei)似維(wei)度(du)(du)表(biao)(biao),而時間序列數(shu)據(ju)(ju)(ju)類(lei)似事(shi)實(shi)(shi)表(biao)(biao)。但又有所不(bu)同,因為 TDengine 中表(biao)(biao)的(de)(de)(de)數(shu)目是和(he)設(she)備(bei)(bei)數(shu)目相(xiang)同的(de)(de)(de),上(shang)億設(she)備(bei)(bei)就是上(shang)億張表(biao)(biao),這(zhe)樣頻繁創建(jian)、又極(ji)其龐(pang)大的(de)(de)(de)表(biao)(biao),并不(bu)容易處理,主要的(de)(de)(de)麻煩是其產生(sheng)了(le)大量的(de)(de)(de)元數(shu)據(ju)(ju)(ju),超過了(le)單點(dian)的(de)(de)(de)處理能力,這(zhe)就要求 TDengine 能將這(zhe)部分(fen)(fen)元數(shu)據(ju)(ju)(ju)也進行(xing)分(fen)(fen)片(pian)存儲。
當數據與元數據進行分片、多副本操作時,就自然涉及到一致性與可用性的問題。在 TSDB 中,時間序列數據通常是最終一致同步的,因為最終一致算法的吞吐量高延遲低、可用性也比強一致算法好,比如InfluxDB集群版會(hui)用 Dynamo 這(zhe)種無主風格的(de)數(shu)據(ju)(ju)同步。但元數(shu)據(ju)(ju)(也就是我們上面提(ti)到的(de)標簽和表(biao)數(shu)據(ju)(ju))需要(yao)強一致(zhi),強一致(zhi)通常會(hui)用 Raft、Paxos 這(zhe)類算法來保證正確性。
由于元數據量的巨大需要分片,而當時序數據與元數據都做分片(甚至時間序列數據和其關聯的元數據應該在同一分片),但又有截然不同的一致性要求,這就導致 TDengine 的副本復制并不是簡單地使用 Raft 這類算法就能夠駕馭得了的,除非犧牲時序數據的寫入吞吐和可用性,也做強一致復制。這就是 TDengine 使用自研復制算法的根本原因。當然,這些算法在復雜的實時數據庫分布式環境下的(de)一致(zhi)性保(bao)證又是(shi)另外的(de)問題了,也是(shi)我(wo)們(men)要著(zhu)重解決的(de)挑(tiao)戰。


























