No.1-時序數(shù)據(jù)庫隨筆 - 時序數(shù)據(jù)庫綜述
原創(chuàng)?大家好,很開心能夠和大家一起交流時序數(shù)據(jù)庫的相關的內容。
首先還是簡單自我介紹一下,我是 孫金城,花名 金竹。我是2011年加入阿里,在2016年之前一直做公司內部的研發(fā)工作,包括阿里郎,Blink等平臺。
從2016年到現(xiàn)在我一直重心在開源建設上面,包括ApacheFlink/ApacheBeam/ApacheIoTDB,在這個過程中也得到了開源的一些肯定,目前是BeamCommitter,ApacheFlink和ApacheIoTDB的PMC,也是Apache Member,目前全球華人大概有30+的ApacheMember,當然,隨著開源的越來越熱,國內每年參與開源建設的同學也在逐漸的在增加。
那么2020之后會有怎樣的規(guī)劃呢?本著但做好事,莫問前程的心態(tài),會多多在訂閱號中記錄我在流計算和IoT方面的認知。最終努力做到走進阿里/踏入開源,成為最好的自己J
那么為什么一直做流計算會慢慢選擇了解IoT相關領域呢?因為在馬老師看來“5G時代,加速的不僅僅是通訊行業(yè),而是更多的促進物聯(lián)網(wǎng)(IOT)領域的發(fā)展。IoT將是一個新的浪潮。那么我參與IOT領域的切入點是什么呢,就是從了解時序數(shù)據(jù)庫 進行著陸的...
如圖,這不是一篇經(jīng)驗分享,而是一個學習過程分享J
了解時序數(shù)據(jù)庫最先想到的是: 了解一下時序數(shù)據(jù)庫的現(xiàn)狀及發(fā)展趨勢,那么這個數(shù)據(jù)的權威性,大家應該有所共識,在db-engines網(wǎng)站的排名應該很客觀的。如圖,從2018年開始,TimeSeriesDBMS的關注度就持續(xù)迅猛的增長。其實不僅僅db-engines網(wǎng)站有這樣的數(shù)據(jù),Gartner也給出了預見性的判斷。我們一起看一下...
在2019年"賦權的邊緣" 就成為了十大戰(zhàn)略性技術趨勢之一,邊緣由物聯(lián)網(wǎng)驅動,需要使處理計算更接近端而不是集中式的云服務器。當然早在2018年 "云向邊緣計算挺進" 的趨勢就已經(jīng)非常明顯。邊緣設備的信息的收集/存儲和計算需求與日俱增,一直到目前2020年,更多的計算和存儲能力都逐漸下沉到設備端,云邊端一體將是今后幾年持續(xù)的焦點。設備數(shù)據(jù)具備時序性,而作為物聯(lián)網(wǎng)領域具備存儲和計算能力的產(chǎn)品就是時序數(shù)據(jù)庫。那么目前業(yè)界時序數(shù)據(jù)庫的陣營是怎樣的狀況呢?我們繼續(xù)看一下...
同樣出自DB-Engines的排名信息,目前有幾十種(大概34種)時序數(shù)據(jù)庫,大家熟知的InfluxDB自2016年以來穩(wěn)穩(wěn)的位居榜首,隨著2018年IoT領域的崛起,InfluxDB的熱度也持續(xù)飆升,穩(wěn)穩(wěn)地龍頭位置,那么InfluxDB為啥如此受到時序數(shù)據(jù)存儲技術的青睞呢?我們接下來就會和大家細致進行分析...
其實時序數(shù)據(jù)庫早在1999年就已經(jīng)有RRDtool,全稱RoundRobin Database。新數(shù)據(jù)會自動覆蓋老數(shù)據(jù),也就是考慮了時序數(shù)據(jù)的時效性,在2008年又出現(xiàn)了Graphite,Graphite是一個用于采集網(wǎng)站實時信息,并進行統(tǒng)計的開源項目,可用于采集多種網(wǎng)站服務運行狀態(tài)信息。隨后就是大家熟知的OpenTSDB,InfluxDB,還有feacebook的內存版時序數(shù)據(jù)庫,再有就是非常著名的用于監(jiān)控的Prometueus,2017年,2018年的時候時序數(shù)據(jù)庫已經(jīng)發(fā)展的非常迅速來,這個在剛才的DB-engines網(wǎng)站數(shù)據(jù)和Gartner給出的戰(zhàn)略技術趨勢信息是非常吻合的。那么在2020年,Apache開源社區(qū)又出現(xiàn)了時序數(shù)據(jù)庫的黑馬 ApacheIoTDB,將來還會有哪些時序數(shù)據(jù)庫產(chǎn)品呢?我們讓時間來揭曉。J
那么,在這里,拋出一個問題,就是:“時序數(shù)據(jù)庫難道不能直接存儲到關系數(shù)據(jù)庫嗎?”為啥要造出很多時序數(shù)據(jù)庫,時序數(shù)據(jù)庫和關系數(shù)據(jù)庫又有怎樣的本質區(qū)別呢?
其實數(shù)據(jù)存到哪里合適,還是要看數(shù)據(jù)本身的特點,以及數(shù)據(jù)處理的需求。面對IoT領域,時序數(shù)據(jù)有很多的數(shù)據(jù)來源,比如汽車,火車,飛機等交通工具,以及我們越來越被大家認可的智能家居產(chǎn)品等。
當然最大的數(shù)據(jù)量來源還是工業(yè)領域的各種設備傳感器數(shù)據(jù),這些設備的工況數(shù)據(jù)收集和處理將給存儲和計算帶來巨大的挑戰(zhàn)。
我們以一個具體的案例來說,這是GoldWind發(fā)電數(shù)據(jù)采集,GoldWind有超過2w個風機,一個風機有120-510個傳感器,采集頻率高達50Hz,就是每個傳感器1秒50個數(shù)據(jù)點采集峰值。這要算下來就是每秒5億個時序指標點的數(shù)據(jù)。
這個數(shù)據(jù)量讓數(shù)據(jù)采集/存儲/計算面臨很大的挑戰(zhàn)。同時還有我們業(yè)務中的一些非常常見的采集/查詢需求,20萬/妙的吞肚需求是非常常見的,但是這樣的需求單機關系數(shù)據(jù)庫也是很難搞定的,我們接下來會分析一下具體原因。
好的,那么還是簡單梳理一下時序數(shù)據(jù)的特點,首先時序數(shù)據(jù)有特定的一些概念。如圖:Metric,就是我們要采集的指標,類似一張表,還有Tag,就是metric的屬性特點,比如指標屬于哪個設備,哪個區(qū)域等等維度信息。再有Field就是真實要采集的指標名稱。那么非常重要的時序數(shù)據(jù)一定有數(shù)據(jù)產(chǎn)生的時間,就是Timestamp時間戳信息,指標數(shù)據(jù)的時效性也是非常重要的,所以要有時間信息。那么Point就是類似表的一行數(shù)據(jù)。我們以風力 指標 為例 簡單圖示一下這些概念。
Wind-force就是Metric,其中 city,region就是Tag,speed和direction就是Field。當然Timestamp不用多說就是指標數(shù)據(jù)產(chǎn)生的時間了。
那么這種關系設計的方式大家不難發(fā)現(xiàn),tag的數(shù)據(jù)存儲會有很多的冗余。這是關系數(shù)據(jù)庫存儲時序數(shù)據(jù)的一個涉及到存儲成本的問題。當然問題不僅如此,我們繼續(xù)往下看。
現(xiàn)在,我們簡單總結一下時序數(shù)據(jù)場景的特點或者說是需求核心會涉及到寫入/讀取/存儲成本三個維度。寫入要支撐上千萬甚至上億的吞吐能力;讀取就需要根據(jù)不同的業(yè)務有不同的讀取需求,后面我們會慢慢分析到。當然面對成本問題,海量數(shù)據(jù)的存儲成本無疑是時序數(shù)據(jù)庫設計的重中之重。所以,面對這樣的時序數(shù)據(jù)場景的特點,時序數(shù)據(jù)存儲到關系數(shù)據(jù)庫就會有很多弊端,比如剛才說的數(shù)據(jù)冗余造成的成本問題,還有寫入的吞吐問題等,那么我們接下來就要討論這些問題存在的本質原因...
好,回過頭來我們在來看看目前的時序數(shù)據(jù)庫從架構的角度有哪幾種?
第一種,就是在關系數(shù)據(jù)庫基礎上進行改進的時序數(shù)據(jù)庫,比如基于PG開發(fā)的Timescale。
第二種,就是在KV數(shù)據(jù)庫的基礎之上進行改進的時序數(shù)據(jù)庫,比如,基于HBase開發(fā)的OpenTSDB。
第三種,就是為時序數(shù)據(jù)量身定制的時序數(shù)據(jù)庫,那么目前的領頭羊就是InfluxDB。當然,我前面說過,我也很看好2020新晉的Apache頂級項目ApacheIoTDB。
這三類時序數(shù)據(jù)庫又怎樣的特點呢,我們接下來逐一進行討論分析...
我們先來看看基于關系數(shù)據(jù)庫的時序數(shù)據(jù)庫,既然基于關系數(shù)據(jù)庫,那么我就要聊聊和關系數(shù)據(jù)庫密切相關的存儲結構。
首先我們看一個簡單的二叉樹,我們知道二叉樹是樹形結構的一個重要類型。許多實際問題抽象出來的數(shù)據(jù)結構往往是二叉樹形式,而且二叉樹的存儲結構及其算法都較為簡單,二叉樹特點是每個結點最多只能有兩棵子樹,且有左右之分。那么我們看看怎樣向二叉樹里面插入一個數(shù)據(jù)點呢?
如圖,我們插入節(jié)點10,插入的過程是二分法,即使從整個節(jié)點的數(shù)值的中間開始,如果要插入的值比這個數(shù)小就從左子樹繼續(xù)二分查找,如果要插入的數(shù)據(jù)比當前數(shù)大,就從右子樹繼續(xù)查找,那么按照這個算法,二叉樹的寫入和查詢的復雜度都是logN。
我們再舉一個小例子,可能目前我們講解的內容很簡單,大家都很熟悉,不過為了為后面的內容做鋪墊,大家還是稍微耐心聽我多啰嗦幾句。這個例子,就是假設我們有如圖8個數(shù)據(jù),我們如果想要查找數(shù)據(jù)2,那么我們先看中間數(shù)據(jù)(第4個)點,13,比較一下13比2大,所以要查詢的2一定在13的左邊,我們再從左邊的數(shù)據(jù)進行二分查找,左邊的中間數(shù)據(jù)5,發(fā)現(xiàn)5還是比2大,那么就需要查找左子樹,目前只剩下了2,當然也就是我們要找的數(shù)據(jù)了。那么logN其實就是log2N對吧,集合8個數(shù)據(jù),查找一條數(shù)據(jù)就是log8,2的3次方等于8,所以log8就是3次,這是二叉樹的查詢復雜度的一個簡單示例說明。OK,這個二叉樹介紹呢有點浪費大家時間,廢話有點多,大家見諒,那么,接下來我們就介紹關系數(shù)據(jù)庫中使用的存儲數(shù)據(jù)結構。
在實際的關系數(shù)據(jù)庫中有2種數(shù)據(jù)存儲結構,一個是BTree一個是B+Tree,那么,這兩種數(shù)據(jù)結構格局特色,都有使用的場景。那么這兩種數(shù)據(jù)結構的查詢復雜度是怎樣的呢?有什么本質區(qū)別。
BTree的寫入成本是LogBN,B就是樹的階數(shù),如圖就是3階BTree,每個節(jié)點的黃色方框表示的指針個數(shù)就是樹的階數(shù),藍色部分就是具體構建BTree節(jié)點的數(shù)值,如果是索引樹的話,藍色部分就是構建索引的key的值,比如訂單號之類的關系數(shù)據(jù)庫的索引字段。
B+Tree的寫入成本是LogBN,如果都是LogBN那么Btree和T+Tree有啥本質區(qū)別呢?這要看看BTree和B+Tree從數(shù)據(jù)的結構上有啥區(qū)別?
最本質的區(qū)別是BTree在每個樹節(jié)點是有數(shù)據(jù)存放的,B+Tree只有葉子節(jié)點才會存放數(shù)據(jù),我們說的數(shù)據(jù)就是關系數(shù)據(jù)庫中一行數(shù)據(jù)(包括Key和普通字段),比如訂單號是key,訂單號以及訂單對應的產(chǎn)品名稱,數(shù)量,金額等就是數(shù)據(jù)。同時還有一個很大的區(qū)別就是B+Tree結構在樹葉節(jié)點是有指針指向相鄰的葉子節(jié)點的,是一個鏈表。這特點,大家想想有什么利好?對,這個特點是有助于區(qū)間查詢的。大家花幾秒鐘時間思考一下,相對于BTree是不是有這樣的優(yōu)勢?:)
這里大家可能發(fā)現(xiàn)一個問題,就是這個算法復雜度應該是LogN,為啥是LogBN。本質上在存儲角度我們考慮的是DAM模型,也就是計算磁盤訪問次數(shù)的成本。
我們繼續(xù)思考另外一個有意思的問題?Btree的成本和B+Tree都一樣,B+Tree又有友好支持區(qū)間查詢的優(yōu)勢,那么兩個共存的意義是什么呢?好,我們還需要繼續(xù)搞清楚這件事情...其實對于不太了解這些數(shù)據(jù)結構的同學是有點燒腦的,對于熟悉的同學可以放松一下,我們繼續(xù)探究B+Tree存在的意義,拋開算法復雜度之外,還有哪些其他存儲領域的實際因素,導致B+Tree更有優(yōu)勢。
上面是BTree從算法角度的復雜度推導,下面是B+Tree的...
這里有一個地方要說明一下,這里推理的復雜度是logN,但是前面我們說的是logBN,這個差別是什么?其實本質前面是磁盤訪問復雜度,不包括數(shù)據(jù)塊內的key值查找。這個大家如過感興趣,可以思考一下,如果有問題歡迎留言J
OK,放松3秒鐘,然后繼續(xù)分析 B+ Tree ?我們繼續(xù)和這個問題死磕...
要想清楚上面的問題,我們還需要一些算法之外的輔助內容,雖然是輔助但卻是和存儲系統(tǒng)密切相關的部分。那就是磁盤。在存儲領域選擇BTree還是B+Tree和磁盤IO有著直接的關系。那么什么是磁盤IO呢?
我們知道計算機的CPU就如同人的大腦,大腦根據(jù)外部輸入進行思考,并為我們下達行為命令,那么CUP同樣要依托于外部輸入進行計算,然后將計算的結果再向外輸出。那么我們存儲里面的磁盤IO可以簡單理解成從磁盤讀取數(shù)據(jù)和向磁盤寫數(shù)據(jù)的過程。
這個圖應該是我們上學課本或者數(shù)據(jù)庫理論方面書籍里的圖片,處理器,數(shù)據(jù)總線,主存,磁盤等等,我們再熟悉不過的名詞,當然內部也是非常復雜的,我們今天挑與我們要討論的問題相關的內容進行分析。
那就聊聊磁盤,首先磁盤內有很多的盤片,每個盤片又由若干扇區(qū)組成,扇區(qū)是磁盤讀寫的最小單元,每個扇區(qū)多大呢?一般是512個字節(jié)。好,那么這個和數(shù)據(jù)庫又有啥關系呢?
數(shù)據(jù)庫的數(shù)據(jù)就存儲在磁盤上,但是從數(shù)據(jù)庫系統(tǒng)角度如果每次讀取一個扇區(qū)的數(shù)據(jù)就太慢了,所以數(shù)據(jù)庫一般會有數(shù)據(jù)塊的抽象設計,數(shù)據(jù)都是一個數(shù)據(jù)塊讀取一次磁盤,一般數(shù)據(jù)塊大小是4~64KB,那么重點來了,通常讀取一個數(shù)據(jù)塊的磁盤IO需要消耗大概10ms左右的時間,這是非常耗時的操作了,所以我們在數(shù)據(jù)庫設計的時候一定要盡量減少磁盤的訪問次數(shù)。那么MySQL的InnoDB默認數(shù)據(jù)塊的大小是16KB,當然這是可以配置的。不同數(shù)據(jù)庫默認值不一樣,比如HBase存儲的數(shù)據(jù)塊大小應該默認64M。好的,說到這里,不知道大家是不是知道我接下來要說什么了?有這方面經(jīng)驗的估計秒懂,但是第一次思考這個問題的,還是需要些解釋的,我們往下繼續(xù)進行...
OK,我們還是舉一個例子來討論,假設以16K為塊大小,也就是每16K數(shù)據(jù)需要訪問一次磁盤,我們的目標是減少磁盤I/0,那么數(shù)據(jù)相同的情況下,B-Tree和B+Tree哪個磁盤I/O更少呢?假設我們有一個查詢:select* from device where deviceId=38deviceID是一個bigint的8字節(jié)類型,作為索引key。我們分別分析一下,Btree和B+Tree哪個訪問磁盤更少?
主鍵deviceId為bigint類型,長度為8字節(jié),而指針大小在InnoDB源碼中設置為6字節(jié),這樣一共14字節(jié),也即是如圖一個藍色框數(shù)據(jù)和一個橙色框指針需要14個字節(jié)存儲。那么,我們一個數(shù)據(jù)塊中能存放多少這樣的單元,就代表一次可以在內存加載多少數(shù)據(jù)key(deviceID)和對應的地址指針,即16384(16K)/14bytes=1170。也即是,每1170個設備id讀取一次磁盤,那么也就是內存構建一個1170階的B+Tree。
那么大家想,如果是BTree結構,一行數(shù)據(jù)除了設備Id還有他設備信息,那么Btree在沒有節(jié)點不但存儲key和指針,還要存儲數(shù)據(jù),所以同樣的數(shù)據(jù)塊大小,是不是一次加載到內存的Key數(shù)量比B+tree要少很多,那么在查詢時候必然比B+Tree訪問磁盤IO要多,大家思考2秒鐘,是不是這個道理?:)
那么如果是BTree,相同的塊大小,樹的階數(shù)就比較小,相對于B+Tree來說,相同條件B+Tree的階數(shù)更大,也就是復雜度上的logBN,的B比較較大,B越大,相同的查詢開銷越小。
那么,既然B+tree很優(yōu)秀,我們看看一個B+Tree能支持怎樣的數(shù)據(jù)規(guī)模呢?好我們想想B+Tree只有葉子節(jié)點要數(shù)據(jù),比如我們一行數(shù)據(jù)是1K,一個數(shù)塊是16K,那么也即是一個數(shù)據(jù)塊可以存放16行數(shù)據(jù)。那么一個3層的B+Tree,葉子節(jié)點有多少行記錄呢?三層的樹的話,第二層有多少數(shù)據(jù)指針指向第三層葉子節(jié)點就有多少數(shù)據(jù)塊數(shù)據(jù)的數(shù)據(jù),每個葉子是一個數(shù)據(jù)塊,每塊有16行數(shù)據(jù)就能計算數(shù)據(jù)規(guī)模了。那么底層有多少指針呢?還是按照剛才MySQLinnoDB的例子算,會構建一個1170階的B+Tree,大家還記得“階”的概念吧,就是一個節(jié)點指針的數(shù)量,那么1170階的B+tree,第二層首先有1170個節(jié)點,每個節(jié)點又有1170個指針(剛才我們計算過),那么我們可以計算第三層的記錄行數(shù)了,那就是:1170*1170*16=2000w+。也即是3層的1170階B+tree可以完美的支持2000w的數(shù)據(jù)量,那么這2000w數(shù)據(jù)量查詢一條數(shù)據(jù)的成本是多少呢?通常磁盤IO的代價是最大的,剛才我們說的了一次磁盤大概10ms,那么3層,我們最多要3次磁盤IO就可以查詢到了。也就是毫秒級的查詢延時。
我們再看一個例子,分析為啥3次就夠了,假設這棵樹,我們要查找key為30的數(shù)據(jù),那么過程如下。
根據(jù)根結點指針找到文件目錄的根磁盤塊1,將其中的信息導入內存。[1次磁盤IO] 此時內存中有三個key(5,28,65)和三個存儲其他磁盤塊的地址的數(shù)據(jù)。我們發(fā)現(xiàn)28<30<65,因此我們找到指針p2。
- 根據(jù)p2指針,我們定位到磁盤塊3,并將其中的信息導入內存。 [2次磁盤IO] 此時內存中有3個key(28,35,56)和三個存儲其他磁盤塊的地址的數(shù)據(jù)。我們發(fā)現(xiàn)28<30<35,因此我們找到指針p1。
- 根據(jù)p1指針,我們定位到磁盤塊8,并將其中的信息導入內存。 [3次磁盤IO] 此時內存中有3個key(28,30,33)和對應的數(shù)據(jù),完成查找。
OK,查詢的邏輯和成本消耗先介紹到這里。下面更重要的內容來了,也就是內容會涉及到了為什么關系數(shù)據(jù)庫不適合作為IoT場景的存儲,大家要多花精力理解,我給大家演示一下BTree的寫入的過程。。。
好,接下來,假設我們有數(shù)據(jù)按照如圖的順序到來,如何一步,一步的構建一顆BTree索引樹呢?
好,首先來的數(shù)據(jù)是3,35,90我們構建一個樹的形態(tài)如圖,35是root,左右各有一個孩子。
然后又來了17和26,二分的方式,17和26都小于35,所以都在35的左子樹,但是這里有個問題?
就是我們是一個3階Btree,指針有3個,節(jié)點只能有2個,所以3,17,26已經(jīng)超了,我們要進行節(jié)點的分裂,后面存儲層面就涉及到磁盤塊的分裂。
好,我們進行節(jié)點分裂之后,樹的樣子變成,root節(jié)點有2個key3個指針。我們繼續(xù)看后面的變化。。。
我們看看后面來了哪些數(shù)據(jù)。。。樹的變化如圖,這里提醒一點,中序遍歷的不同的樹形狀可以得到相同的結果。我們這個例子核心是想說明數(shù)據(jù)的構建過程有節(jié)點的分裂,也意味著存儲有磁盤塊的分裂,會產(chǎn)生大量的隨機磁盤IO。我們繼續(xù)看變化。。。
接下來再后面的數(shù)據(jù)。。好,36,87,29,65,75到來后,樹的形狀如圖。。。(這棵樹是故意弄的很平衡哈:)
最后,我們看看有哪些數(shù)據(jù)到來,最終樹的形成。基于B-Tree/B+Tree的存儲,數(shù)據(jù)寫入造成很多的隨機IO。我簡單說一下,大家思考,如果一個節(jié)點已經(jīng)寫入磁盤了,后面樹形發(fā)生變化之后,節(jié)點分裂,原先存儲到一個磁盤塊的數(shù)據(jù),就會分開存儲到2個新的磁盤塊,那么原先的磁盤塊就要刪除。這樣反復的操作,那就造成了Btree/B+tree很多的隨機IO了。
OK,聊到隨機IO對存儲系統(tǒng)的影響,我們也要順便說一下相對于隨機I/O就是順序I/O。
- 順序IO - 是本次 I/O 給出的初始扇區(qū)地址和上一次 I/O 的結束扇區(qū)地址是完全連續(xù)或者相隔不多的。在順序I/O訪問中,磁盤所需的磁道搜索時間較少,讀/寫磁頭可以以最小的移動訪問下一個塊。
- 隨機IO - 是指讀寫操作時間連續(xù),但訪問扇區(qū)地址不連續(xù),隨機分布在磁盤LUN的地址空間中。
隨機I/O可能是因為磁盤碎片導致磁盤空間不連續(xù),或者當前block空間小于文件大小導致的。連續(xù) I/O 比隨機 I/O 效率高的原因是:在做連續(xù) I/O 的時候,磁頭幾乎不用換道,或者換道的時間很短;而對于隨機 I/O,如果I/O 很頻繁的話,會導致磁頭不停地換道,造成效率的極大降低。如圖,我們看到不同磁盤 順序寫和隨機寫的性能差異,差不多都是數(shù)量級的差距。即便SSD的硬盤,希捷公司也有明確的說明,大家可以參考鏈接查看測試數(shù)據(jù)。
所以隨機IO和順序IO對存儲系統(tǒng)的寫入性能有很大的影響。那么這些差異和時序數(shù)據(jù)場景有什么關系呢?我們繼續(xù)聊。。
首先我們還是要切回時序數(shù)據(jù)場景的特點,IoT場景是寫多/讀少的場景,寫入性能至關重要。所以基于關系數(shù)庫,也就是Btree的存儲結構的時序數(shù)據(jù)塊在存儲時序數(shù)據(jù)時候都存在IO效率低下,寫入速度很慢的現(xiàn)象。那么需要如何解決呢?
解決BTree的寫入問題一般有兩種方式,一種是是COLA(Cache-Oblivious Look ahead Array)- tokuDB。另一類就是我們今天要重點分析的,LSM tree(Log-structured merge Tree)結構。這個數(shù)據(jù)結構的插入復雜度是O(1),這個比B+Tree的logN要優(yōu)秀很多了。
目前基于LSM Tree數(shù)據(jù)結構的存儲有很多,如著名的KV存儲HBase還有LEVELDB,RocksDB等等。
那么為了解決時序數(shù)據(jù)寫入問題,時序數(shù)據(jù)庫陣營出現(xiàn)了一些基于KV存儲的時序數(shù)據(jù)庫,比如大家熟知的,基于HBase的OpenTSDB。
LSM Tree全稱是 Log-structured merge Tree,Merge就是合并,Log structured就是像寫日志一樣進行順序寫入,append only的方式。
LSM樹的核心思想就是放棄部分讀能力,換取寫入能力的最大化。核心思路其實非常簡單,就是假定內存足夠大,因此不需要每次有數(shù)據(jù)更新就必須將數(shù)據(jù)寫入到磁盤中,而可以先將最新的數(shù)據(jù)駐留在內存中,等到積累到一定程度后,再使用歸并排序的方式將內存中的數(shù)據(jù)合并追加到磁盤隊尾。我們來看一下這個基于LSM Tree的寫入過程:
首先我們要解決如何做到順序IO,就是一個數(shù)據(jù)寫入到來之后,先進行WAL的落盤。那么如何解決亂序?那就是,寫WAL是為了恢復,真正的有序寫入要將數(shù)據(jù)寫入內存,也就是Mem-Table,然后對Mem-Table進行排序,數(shù)據(jù)寫入到內存之后,就表示寫入成功了。那么寫到內存之后會怎樣操作呢?就是要解決落盤問題。當內存數(shù)據(jù)到達一定規(guī)模,就需要寫入磁盤,LSM Tree的做法是將要刷磁盤的Mem-Table變成immutable,刷磁盤同時不影響寫入請求,在創(chuàng)建一個新的Mem-table。這樣的持久化在數(shù)據(jù)持續(xù)變化和查詢的時候會有什么問題嗎?當然有,比如,key更新/刪除了怎么辦?要查詢的key在多個文件怎么辦?所以,LSM Tree結構還需要有一個Compactions的階段,以及對數(shù)據(jù)創(chuàng)建索引的過程。我們以HBase的HFile為例,存儲結構如圖,文件中除了數(shù)據(jù)還需要有索引和Boolmfilter的機制進行加速查詢。
上面我們了解了LSM Tree的寫入邏輯,那么查詢邏輯是怎么樣的呢?查詢邏輯最核心的是要查詢索引,首先在內存Mem-table里面查詢,然后在immutable Mem-table里面進行查找,然后是磁盤Flie里面進行查找。當然這里有Bloom filter輔助查詢。Bloom filter本質就是一個bitmap,每個key數(shù)據(jù)用k個獨立的hash就行計算,填充bitmap,數(shù)據(jù)查詢時候Bloomfilter說沒有一定沒有,Bloomfilter說有,不一定有,還要繼續(xù)索引查找。
LSMTree雖然以犧牲查詢?yōu)榇鷥r來解決Btree寫入問題,但是會利用一些輔助手段,比如索引,Bloomfilter基數(shù)估計等手段加速查詢,同時各種數(shù)據(jù)庫還會有不同的Cache機制加速查詢。這樣聽起來基于LSMTree結構的KV存儲應該有很不錯的讀寫表現(xiàn)。那么基于KV存儲的時序數(shù)據(jù)庫應該怎樣設計呢?
我們還是以大家熟知的基于HBase的OpenTSDB為例來進行分析,看看KV存儲在設計時序數(shù)據(jù)存儲時候的設計技巧和存在的問題。
以HBase為例,我們聊聊KV存儲對LSMTree的應用,如圖大家應該很熟悉,HBaseClient向HBase的RegionServer發(fā)起讀寫請求,當然HLog就是LSMTree中的WAL部分,MemStore就相當于LSM里面的Mem-table部分,HFile就是持久化文件。
簡化一下就是HBase的HRegionServer包括HLog和HRegion,一個HRegion包括很多的HStore又分為MemStore和HFile兩個核心部分。
其中按照Rowkey進行region劃分,每個region對應一個ColumnFamily,一個MemStore。
每個ColumnFamily里面又有若干Qualifiler。當然任何一個KV數(shù)據(jù)又會包含一個時間戳Timestamp。
那么按照這個架構,一條完整的數(shù)據(jù)是怎樣的呢?
如圖這是一條完整的KV數(shù)據(jù),開頭KeyLength和ValueLength:兩個固定的長度,分別代表Key和Value的長度。
在Key部分:RowLength是固定長度的數(shù)值,表示RowKey的長度,Row 就是RowKey的值。
Column Family Length是固定長度的數(shù)值,表示ColumnFamily的長度
接著就是ColumnFamily,再接著是Qualifier,然后是兩個固定長度的數(shù)值,表示TimeStamp和KeyType(Put/Delete)
Value部分沒有什么復雜的結構,就是純粹的二進制數(shù)據(jù),這個也是很大的成本問題,后面我們會介紹。
那么這里有兩個常見問題,一是按Rowkey進行Region的劃分,熱點問題怎么處理?二是按ColumnFamily進行Store存儲,如何設計ColumnFamily包含的Qualifier?這個磁盤塊,磁盤IO有什么關系?
我們知道HBase的Rowkey都是字典序的,一般Rowkey的典型組成會是:業(yè)務KEY+時間倒序(Long.MAX_VALUE -timestamp)/加鹽/Hash組成,那么時間倒序有利于最新的數(shù)據(jù)在最前面,加鹽或者Hash可以緩解熱點問題。關于ColumnFamily的Qualifier的設計,簡單講一個原則就是經(jīng)常一起查詢的數(shù)據(jù)要作為同一個ColumFamily的Qualifier。
除了這兩個問題,我們還有2個問題需要考慮,那就是數(shù)據(jù)查詢時候如何定位一條數(shù)據(jù)屬于哪個Region?HFile里面的KV數(shù)據(jù)如何快速讀取?
回答Region尋址和HFile快速讀取的問題,也要分析HBase對Region的管理方式,Region信息屬于HBase的內部抽象,對Region的管理HBase也建立了一顆B+樹,我們前面聊過,如果一個數(shù)據(jù)塊事16K,那么一個3階的B+Tree能容納2000w的數(shù)據(jù)量,一次查詢只需要3次IO,那么HBase數(shù)據(jù)塊默認大小事64M(可以配置更大),那么這些Region一般會加載在內存中,所以在B+Tree的數(shù)據(jù)機構上HBase對region的尋址非??焖佟?/p>
同樣在HFile里面定位rowkey的位置也是基于二分查找的,如圖查詢Rowkey為fb的過程,圖中紅色箭頭部分,第一層f在a和m之間,所以查詢a的子樹,第二層f在d和h之間,所以查詢d所指向的子樹,第三層我就找到了f這個值,然后在第四層在查找fb數(shù)據(jù)最終返回數(shù)據(jù)。
這里提醒一下HBase存儲是LSMTree數(shù)據(jù)結構,但是為了加速查詢內部對Rowkey的索引建立和Region的管理都是采用了B+Tree的數(shù)據(jù)結構。
好了,解了HBase的基本內容之后,我們思考一下,我們如何基于HBase設計一個時序數(shù)據(jù)庫呢,首先要思考的問題就是,如何在HBase中對時序數(shù)據(jù)的核心概念進行抽象映射。也就是時序數(shù)據(jù)核心概念是Metric相關內容,Tag相關內容,和Timestamp時間戳。HBase現(xiàn)有的概念是ColumnFamily,ColumnQualifier當然也具備Timestamp信息。
那么我們無法改變HBase的KV數(shù)據(jù)結構,只能在表達時間序列數(shù)據(jù)的同時最充分的利用HBase現(xiàn)有的數(shù)據(jù)結構抽象,最核心的就是Rowkey的設計和ColumnFamily和ColumnQualifier的利用技巧。我們逐一看一下。
那么基于HBase的時序數(shù)據(jù)庫OpenTSDB是如何設計的呢?
我們先看最核心的OpenTSDB的RowKey Schema的設計,其組成是salt] <metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>]
那么,按照HBase的思維這里有一個很奇怪的問題,就是【salt】設計竟然放在了rowkey的最前面,這個Salt解決了什么問題,同時有帶來了什么問題呢?
當然Salt部分和前面我說的HBaseRowkey里面也可以加Salt的作用是一樣的,解決熱點問題。但是這里問題是,在查詢的時候用戶不知道salt的值如果進行查詢呢?這肯定是帶來了數(shù)據(jù)查詢的性能問題。其實OpenTSDB里面Salt是分桶的抽象,默認20個桶,用戶可以配置,那么查詢時候同一個業(yè)務rowkey就要查詢20次然后在進行merge,這個會極大的影響get查詢。
好,那么通常Rowkey設計不攜帶salt部分,同時OpenTSDB在Rowkey設計上也下了不少功夫,首先大家看到Rowkey的開始是一個metric的uid。
如圖,OpenTSDB里面的一個rowkey示例,包括 metric部分,時間戳部分,tagk和tagv的組成。
我們以前面我們風力數(shù)據(jù)采集的信息為例,Rowkey就應該是speed+ts+city+hangzhou+region+xihu。
那么這里面我們還沒有解釋uid的作用,后面我們慢慢進行說明?,F(xiàn)在要考慮的問題是,Metric&Time&Tag這個在rowkey中的順序可以變化嗎?或者說如何設計最合理呢?
HBase是按照rowkey的字典序順序排列的,為了減少查詢IO,最好將相同的Metric寫到相同的磁盤塊,所以OpenTSDB需要將Metric作為Rowkey的最前面。
Timestamp沒有業(yè)務語義,不適合放到最前面(如何放到前面可能出現(xiàn),多個Metric會混雜在一個磁盤塊,不利于查詢,寫入也會造成熱點問題)
Tags放在最前面,會造成大量的Scan操作,比如,用戶指定多個Tags中的某個查詢,而且不是前綴Tag的話,就會在HBase里面將會變成大范圍的掃描過濾查詢,當然Tags放到Timestamp前面也存在Scan的問題。
好,思考了這些現(xiàn)實問題,OpenTSDB對rowkey的設計就是根據(jù)用戶的metric+ts+tag信息的方式組成。那么目前看這個rowkey的設計還是很合理的,那么是否也在海量的時序數(shù)據(jù)場景有一些明顯弊端呢?
很明顯,這個key組成里面根據(jù)業(yè)務的不同會變得復雜,首先就是HBase的key里面有timestamp,是一定要有的,然后業(yè)務的rowkey里面也是有時間戳信息的。
為了套用先有的HBase結構,存儲中有很多無用的信息,比如comumnFamily部分,KeyType部分等。同時Rowkey里面的metric是一個業(yè)務字符串,這些數(shù)據(jù)在實際存儲過程中很多冗余,造成存儲成本的浪費。
還有HBase是弱類型問題,不能對Value部分根據(jù)業(yè)務的不同字段類型進行專門的壓縮。同時,Rowkey部分包含很多tag信息,沒有對Rowkey和tag進行倒排索引,同樣使得查詢受限。大量scan操作性能很差。所以基于KV的時序數(shù)據(jù)庫存在客觀的設計弊端。
面對HBase設計時序數(shù)據(jù)存儲的弊端,OpenTSDB做了哪些比較核心的優(yōu)化呢?
首先是Rowkey里面的Timestamp時間粒度不是毫秒,而是小時,這樣極大的減小了scan的部分,一小時的數(shù)據(jù)可以一個get進行獲取。另外我們一直提到的metric_uid,其實也是對存儲的優(yōu)化,將業(yè)務字符串映射成uid,可以極大簡化存儲。還有在Compactions部分,會將很多Qualifier數(shù)據(jù)合并成一個Qualifier里面,提高壓縮效率。
我們細致的看一下OpenTSDB的data Scheam和uid mapping的Schema。
最核心要了解的就是RowKey部分,我們看到很多的編碼,我們看到編碼后的rowkey和上面編碼前的字符串有極大的改進。
關于UID,OpenTSDB里面采用3個字節(jié)存儲,約1600萬個值,足夠滿足大部分業(yè)務場景,同時我們也可以根據(jù)業(yè)務規(guī)模改變UID的生產(chǎn)策略。這樣極大的節(jié)省存儲和key排序成本。
同時我們看到,TSDB-UID的映射表,是雙向的,既可以同UID找到key,也可以通過key找到UID。大大提高搜索效率。
OK,坦白講,不論OpenTSDB怎么優(yōu)化,首先都需要先填補HBase設計在時序數(shù)據(jù)庫場景適配的坑,再進行優(yōu)勢發(fā)揮,那么這樣的客觀事實,勢必會催生為IoT時序數(shù)據(jù)而生的原生時序數(shù)據(jù)庫。
一個是大家熟知的時序數(shù)據(jù)庫領域的領頭羊Influxdb,另一個是2020年Apache 新晉的頂級項目ApacheIoTDB。我們分別探討一下。
InfluxDB在時序數(shù)據(jù)庫排名第一,這個成績不是浪得虛名,而是實至名歸,有句話叫,天才出于勤奮,其實InfluxDB從誕生伊始,一直到現(xiàn)在都在不停的努力,不停的為解決現(xiàn)實問題而優(yōu)化改進。我們接下來都是基于InfluxDB1.8版本進行描述的。
我們了解一下Influxdb 引擎升級的歷程,最初為避免B+Tree帶來的寫入問題,InfluxDB采用了LSMTree的存儲設計,底層引擎采用LevelDB,但是后面發(fā)現(xiàn)LevelDB存儲不能熱備份問題,于是底層引擎又采用RocksDB解決,但是RocksDB當時又存在時序數(shù)據(jù)場景冷數(shù)據(jù)的高效刪除問題,我們知道海量的數(shù)據(jù)通過api的方式逐條刪除是相當耗時的(會死人的),所以InfuxDB又進行改進引入Shard,每個shard存儲一片時間連續(xù)的數(shù)據(jù),冷數(shù)據(jù)都是時間較老的數(shù)據(jù),一個shard對應一個底層LevelDB數(shù)據(jù)庫,要刪除的時候只需要關庫,刪文件即可。但是這個設計又帶來了新的問題,就是系統(tǒng)的文件句柄問題,后面又有BoltdDB來解決,但是BoltDB里面利用了mmap和B+Tree的數(shù)據(jù)結構,B+Tree的隨機寫問題又糙成了IOPS限制問題,所謂痛苦造就成就,種種投產(chǎn)問題推升了目前InfulxDB全新的存儲和索引架構,TSM架構。
接下來我們看看目前InfluxDB的TSM架構細節(jié),嘗試 盡量 知其然,知其所以然。Lets go...
首先,不可避免的我們要了解InfuxDB針對時序數(shù)據(jù)場景的核心概念的抽象,我們看InfluxDB新增了一些抽象,一個是database,一個是retentionpolicy,尤其retentionpolich是專門針對時序數(shù)據(jù)的冷數(shù)據(jù)設計的,原生的時序數(shù)據(jù)庫的優(yōu)勢就是在設計之初就會考慮時序場景的種種典型問題。那么這些概念的層次結構是怎樣的呢?
首先Database是一個最上層的抽象,或者說是管理單元,然后數(shù)據(jù)也是要按時間進行Sharding的,每個shard都歸屬于一個ShardGroup,然后Shard里面就是具體時序數(shù)據(jù)了,當然包括時間戳,Series和Field信息,其中Series就包含了metric和tag信息。上面的RP就是retentionpolicy。
同時Point相當于一行數(shù)據(jù),如代碼片段所示,包括被排序的key,key的組成是measurement和tags的組合,后面我們還會大量的涉及大key的介紹。好,簡單了解了這個層次邏輯,我們再簡單看看這些概念的代碼抽象。
首先是Store,Store是為Database管理shards和index的抽象。
其中InfluxDB的索引機制有兩種,一是基于內存的版本,一是基于文件的版本,后面我們逐步詳細介紹。
我繼續(xù)向下進行鉆取,那就是文件存儲的分區(qū)管理Shard的代碼抽象,Shard里面會包含時序數(shù)據(jù)和Tag到SeriesKey的倒排索引。大家會發(fā)現(xiàn),有了tag的倒排索引,基于HBase的OpenTSDB里面根據(jù)一部分tag查詢的scan操作就可以避免了。
那么Shard里面還有一個Engine,Engine負責進行合并多shard的查詢結果等工作。Engine代表了一個存儲引擎,這里面包含了TSM架構最核心的組成部分,WAL/CACHE/TSMFile/Compaction/Compression組件。我們后面一一介紹。在往下就是FileStore部分了,F(xiàn)ileStore里面包括很多的TSMFile。那么一個TSMFile是怎樣的結構呢?如圖包括4個部分。
其中Blocks和index部分尤為重要,我們先看看數(shù)據(jù)是如何寫入的...
首先,一個寫請求到來之后的寫入流程如下:
- 第1步,進行AppendOnly的WAL寫入。
- 第2步,然后就要更新索引,這個是加速查詢的本質,包含了tag和serieskey的倒排索引內容。
- 第3步,就是數(shù)據(jù)更新到緩存,相當于LSMTree里面的Mem-table角色。
- 第4步就是返回客戶端成功應答。
當然數(shù)據(jù)不能一直保留在內存,我們需要某種機制進行落盤處理。同時落盤的時候最好不要影響寫入請求。
- 第5步,落盤的過程在InfluxDB里面叫做Snapshotting。進行快照的時機有2個,一個是內存使用達到預定的閾值大小,一個是給定時間間隔沒有數(shù)據(jù)寫入。
- cache-snapshot-memory-size= ”25m”
- cache-snapshot-write-cold-duration= “10m”
落盤之后我們就變成了Level的文件,InfluxDB設計4層的TSMFlile,每個TSMFile內部又有精巧的結構設計。
這里再特別注意一下內存中Cache結構,是一個SortedMap結構。
同時說明一下,在目前InfluxDB(1.8)版本,我沒有看到做快照時候將Cache變成只讀,快照影響寫請求,然后寫完清空Cache。
- 第6步,我想正是因為這個Cache沒有只讀動作,在進行Compaction時候也考慮了低層級采用低CPU消耗的算法,緩解對寫入的影響。而高層級的文件合并就要考慮壓縮成本。
在回到索引部分,我們剛才提到InfluxDB有兩種類型的索引可以選擇,一個是基于內存的,一個是基于文件的。在索引的構建中也絞盡腦汁,利用了可用的一切優(yōu)化手段,B+Tree,BloomFilter,HashIndex等等來加速查詢。
那么,我們來哦細究一下最核心的部分,內存Cache里面的數(shù)據(jù)結構是如何設計的,有怎樣的優(yōu)勢呢?
首先設計的初衷一定是在SeriesKey有序的前提下,高性能寫入。
如圖,Cache內部提供了一個ring結構,來對數(shù)據(jù)進行分桶/分區(qū)管理理論上分區(qū)的數(shù)量最多水256個,為啥呢?因為InfluxDB采用 SeriesKey的前8個bits進行Hash,8個bit最多256個值。而每個Partiton的數(shù)據(jù)存儲是一個sortedmap,包括Series,F(xiàn)ield和時間戳信息。
OK,這里YY一下,按照代碼里面的注釋這個取余操作是可以優(yōu)化的。大家感興趣可以看看源代碼,怎樣優(yōu)化最好。:) 歡迎線下討論...
我們根據(jù)代碼的的分析,可以得到Cache的數(shù)據(jù)結構是這樣的,內存數(shù)據(jù)存儲結構是partiton+map的層級管理,那么這樣的數(shù)據(jù)結構有什么好處呢?
- 環(huán)結構可以Split數(shù)據(jù)結構,降低讀寫時候鎖的競爭
- 取前8個bit進行hash,確保連續(xù)數(shù)據(jù)存儲在一個磁盤塊(時間連續(xù))
- Map是有序的,確保在Snapshoting時候可以快速有序落盤
接下來我們再來看看TSMFile細節(jié)內容。
好,是時候聊聊TSMFile的數(shù)據(jù)結構和對讀/寫性能和存儲成本的設計考慮了。
首先,TSMFile的四核部分中,Header 是5個字節(jié)存放Magic和版本,F(xiàn)ooter是8個字節(jié),記錄TSMFile中索引數(shù)據(jù)在TSMFile的偏移量。Blocks里面存儲很多數(shù)據(jù)塊,索引部分的Key是SeriesKey+Field。
其中IndexEntry對應一個Block進行索引的構建。包括數(shù)據(jù)塊包含數(shù)據(jù)的最小最大時間,以及數(shù)據(jù)塊在TSMFile中的位置。
Block部分除了CRC的數(shù)據(jù)校驗部分,還有數(shù)據(jù)類型,時間戳信息和數(shù)據(jù)值。
InfluxDB目前支持Integer/Float/Boolean/String等數(shù)據(jù)類型,每種數(shù)據(jù)類型都有專門的壓縮算法。
對于不可或缺的Timestamps信息,InfluxDB采用Facebook的Delta-delta算法,極大壓縮有序時間戳的存儲。
同時在Index結構設計上也考慮了時間要素和區(qū)間查詢。索引部分利用minTime為每個block的indexEntry構建基于B+Tree索引樹,一遍更要的加速查詢。
這里,我們提出一個問題,在Footer中記錄整體Index在TSMFile的偏移量,如果我想獲得所有Key,需要怎樣操作?我們看這個數(shù)據(jù)結構,會發(fā)現(xiàn)很明顯是需要根據(jù)offset定位到Index的區(qū)域,然后讀取所有的index數(shù)據(jù)。這樣其實這是一個很耗時,耗空間的操作,需要將整體index進行讀取,那么是否可以優(yōu)化一下呢?
當然,InfluxDB對索引進行了優(yōu)化,為每一個Key的索引信息都添加了offset信息,維護了一張offset表格。
在代碼的抽象上,在IndirectIndex的數(shù)據(jù)結構中有offsets的數(shù)據(jù)維護,這樣想要查詢所有Kyes的時候,我們讀取offsets信息根據(jù)偏移直接獲取對應key的信息。
那么還有什么跟多的設計考慮嗎?
當然,除了剛才我們提到的Level文件合并算法考慮,還有索引優(yōu)化考慮,以及定期的FullCompaction。除此之外,在索引設計上面增加BloomFilter輔助判斷key在索引樹里面的存在性,還利用HashIndex加速key的查找。總體看來InfluxDB在查詢上面花了很多的細節(jié)優(yōu)化。
在繼續(xù)拷問,還有什么其他優(yōu)化嗎?
回答還是肯定的,在Measurement索引區(qū)域進行tag和SeriesKey的倒排索引結構。這在很大程度增加了查詢的靈活性和查詢性能。
同時我們發(fā)現(xiàn)在tagkey到SeriesKey的映射中,我們還有seriesID和SeriesKey的映射維護,這又是出于怎樣的考慮呢?對,這里是對存儲成本也進行了考慮。
這是索引文件TSI的結構設計,包括4個部分,
- 首先是Trailer層面,這是索引的入口,記錄Measurement/Tag/Series三個Block在TSI的偏移量(offset)和大小
- 然后是Measurement block,記錄所有的指標信息,也就是measurement(表)的Meta信息。
- 然后是 tag信息部分,這個部分核心維護了面介紹的倒排數(shù)據(jù)結構<tagkey, <tagvalue, list<seriesID>>>
- 最后series Block索引區(qū)域,記錄了database中所有的SeriesKey信息
當有了SeriesKey和時間戳之后,我們就可以進一步在Cache/File中進行查詢,找到對應的分桶和對應value的時間序列值。
找到對飲的TSMFile對應的數(shù)據(jù)塊之后,加載數(shù)據(jù)塊到內存,根據(jù)TSFMFile中的B+Tree樹索引,二分法找到具體的查詢值。
具體的查詢邏輯如圖標注。
對應TSI的每個部分,內部都有精細的設計和優(yōu)化。這里簡單提兩點:
- 一個是每個部分都有一個Trailer部分保存該部分的組成的偏移量。
- 另一個是,每個部分都利用hashindex加速對應的key查詢。
OK,總之InfluxDB利用TSM和TSI架構,完美解決了高吞吐,熱備份,冷刪除,高性能的查詢,和基于tag的倒排索引索引所支撐了靈活的業(yè)務查詢。
這里特別提醒InfluxDB自動根據(jù)Tag進行倒排索引的建立,在實際業(yè)務中要充分利用。
當然,InfluxDB的龍頭地位除了存儲查詢設計的優(yōu)勢,還有很多實用的功能,比如ContinuousQueries。這個的實現(xiàn)本質就是定時調度,我們可以根據(jù)語法很清楚的了解設計的語義。其中 EVERY是計算頻度,F(xiàn)OR 是計算區(qū)間,GroupBy的時間區(qū)間,就是每次計算的數(shù)據(jù)窗口。
我們簡單看一個示例,EVERY1小時,F(xiàn)or90分鐘,groupby30分鐘。的業(yè)務含有是,每1小時調度一下計算,每次計算的數(shù)據(jù)處理區(qū)間是90分鐘,業(yè)務聚合計算的窗口數(shù)據(jù)30分鐘的數(shù)據(jù)。
我們假設8點中觸發(fā)了計算,那么計算數(shù)據(jù)區(qū)間是6:30~8:00(90分鐘),計算窗口是30分鐘,也就是90分鐘的數(shù)據(jù)計算三次,有三個計算結果。
當然1小時觸發(fā)一次,到9點還會再次出發(fā)計算邏輯。
還有豐富的生態(tài),也是InfluxDB親民的資本。既后數(shù)據(jù)的采集又有基于訂閱的實時計算,還有方便的界面查看。OK,這里可以為InfluxDB點個贊了,很優(yōu)秀。:)
OK,再來看看另外一個Apache開源社區(qū)優(yōu)秀的時序數(shù)據(jù)庫,ApacheIoTDB。
首先是夠新,ApacheIoTDB是2020年9月從Apache 孵化器畢業(yè),成本Apache頂級項目的。
第二是,ApacheIoTDB足夠強大,左邊的寫入和查詢的性能測試已經(jīng)超越了目前的領頭羊InfluxDB。
那么,為什么IoTDB會有這樣的優(yōu)秀表現(xiàn)呢?
首先,ApacheIoTDB也是在LSMTree的基礎進行了架構優(yōu)化,提出了tLSM的存儲架構,在tLSM的架構中,優(yōu)化了LSM中的Mem-table部分,提出了為亂序數(shù)據(jù)提供獨立的處理邏輯,這在寫入上有很大的優(yōu)勢。同時IoTDB更加注重從SQL的角度進行查詢優(yōu)化,讓用戶的查詢邏輯智能的獲取最優(yōu)的查詢性能。OK,我們再看看目前IoTDB整體組件棧是一個怎樣的情況呢?一起來看一下...
圖中,棕色部分是現(xiàn)在發(fā)布的版本已經(jīng)具備的,橙色部分是后續(xù)發(fā)布版本中陸續(xù)規(guī)劃的,當然還有一些有待進一步討論和社區(qū)交流的部分。那么就IoTDB的現(xiàn)有功能來說,已經(jīng)得到了一些工業(yè)企業(yè)的認可,我們一起看一下幾個投產(chǎn)的案例。
這是IoTDB的一部分工業(yè)企業(yè)用戶,包括上海的地鐵項目,這個場景1臺IoTDB實例就支持了每天4000多億的海量時序數(shù)據(jù)采集和存儲。關于IoTDB的分享還有太多的內容可以交流。。。時間原因我們將內容留在下次:)
作者介紹
孫金城,51CTO社區(qū)編輯,Apache Flink PMC 成員,Apache Beam Committer,Apache IoTDB PMC 成員,ALC Beijing 成員,Apache ShenYu 導師,Apache 軟件基金會成員。關注技術領域流計算和時序數(shù)據(jù)存儲。