科技

聽說過時序資料庫嗎?

時序資料庫

即時間序列資料庫,相信大家都知道mysql之列的關係型資料庫,也瞭解redis等nosql資料庫,可是時間序列資料庫,你瞭解多少呢?

這類時間序列資料庫最多,使用也最廣泛。一般人們談論時間序列資料庫的時候指代的就是這一類儲存。按照底層技術不同可以劃分為三類。

直接基於檔案的簡單儲存:RRD Tool,Graphite Whisper。這類工具附屬於監控告警工具,底層沒有一個正規的資料庫引擎。只是簡單的有一個二進位制的檔案結構。

基於K/V資料庫構建:opentsdb(基於hbase),blueflood,kairosDB(基於cassandra),influxdb,prometheus(基於leveldb)

基於關係型資料庫構建:mysql,postgresql 都可以用來儲存時間序列資料

另外一類資料庫其表結構是:

[timestamp] [d1] [d2] .. [dn] [v1] [v2] .. [vn]

其優化的查詢方式不限於查詢原始資料,而是可以組合查詢條件並且做聚合計算,比如:

SELECT d2, sum(v1) / sum(v2) FROM metric WHERE d1 =

“A” AND timestamp >= B AND timestamp

我們希望時間序列資料庫不僅僅可以提供原始資料的查詢,而且要支援對原始資料的聚合能力。這種聚合可以是在入庫階段完成的,所謂物化檢視。也可以是在查詢階段完成,所謂實時聚合。根據實際情況,可以在這兩種方式中進行取捨。

想要在在查詢階段做資料的聚合和轉換,需要能夠支援以下三點。

用索引檢索出行號:能夠從上億條資料中快速過濾出幾百萬的資料。從主儲存按行號載入:能夠快速載入這過濾出的幾百萬條資料到記憶體裡。分散式計算:能夠把這些資料按照GROUP BY 和 SELECT 的要求計算出最終的結果集。

要想盡可能快的完成整個查詢過程,需要在三個環節上都有絕招。傳統上說,這三個步驟是三個不同的技術領域。

檢索:這是搜尋引擎最擅長的領域。代表產品是Lucene。其核心技術是基於高效率資料結構和演算法的倒排索引。載入:這是分析型資料庫最擅長的領域。代表產品是C-store和Monetdb。其核心技術是按列組織的磁碟儲存結構。分散式計算:這是大資料計算引擎最擅長的領域。代表產品是Hadoop和spark。其核心技術是sharding 和 map/reduce等等。前面提到的時間序列庫(比如opentsdb)有不少從功能上來說是沒有問題。它們都支援過濾,也支援過濾之後的聚合計算。在資料量小的時候勉強是可用的。但是如果要實時從十億條裡取百萬記錄出來,再做聚合運算,對於這樣的資料量可能就勉為其難了。滿足海量資料實時聚合要求的資料庫不多,比較常見的有這麼幾種:

基於Lucene構建的“搜尋引擎”:Elasticsearch, Crate.io(雖然是基於Elasticsearch,但是聚合邏輯是自己實現的),Solr;列式儲存資料庫:Vertica(C-store的後裔)Actian(Monetdb的後裔)等; Druid.io。其中Elasticsearch是目前市場上比較很少有的,能夠在檢索載入和分散式計算三個方面都做得一流的資料庫。而且是開源並且免費的。它使用了很多技術來達到飛一般的速度。這些主要的優化措施可以列舉如下。

Lucene的inverted index可以比mysql的b-tree檢索更快。在 Mysql中給兩個欄位獨立建立的索引無法聯合起來使用,必須對聯合查詢的場景建立複合索引。而lucene可以任何AND或者OR組合使用索引進行檢索。Elasticsearch支援nested document,可以把一批資料點巢狀儲存為一個document block,減少需要索引的文件數。Opentsdb不支援二級索引,只有一個基於hbase rowkey的主索引,可以按行的排序順序scan。這使得Opentsdb的tag實現從檢索效率上來說很慢。Mysql 如果經過索引過濾之後仍然要載入很多行的話,出於效率考慮query planner經常會選擇進行全表掃描。所以Mysql的儲存時間序列的最佳實踐是不使用二級索引,只使用clustered index掃描主表。類似於Opentsdb。Lucene 從 4.0 開始支援 DocValues,極大降低了記憶體的佔用,減少了磁碟上的尺寸並且提高了載入資料到記憶體計算的吞吐能力。Lucene支援分segment,Elasticsearch支援分index。Elasticsearch可以把分開的資料當成一張表來查詢和聚合。相比之下Mysql如果自己做分庫分表的時候,聯合查詢不方便。Elasticsearch 從1.0開始支援aggregation,基本上有了普通SQL的聚合能力。從 2.0 開始支援 pipeline aggregation,可以支援類似SQL sub query的巢狀聚合的能力。這種聚合能力相比Crate.io,Solr等同門師兄弟要強大得多。如何快速檢索?

Elasticsearch是通過Lucene的倒排索引技術實現比關係型資料庫更快的過濾。特別是它對多條件的過濾支援非常好,比如年齡在18和30之間,性別為女性這樣的組合查詢。倒排索引很多地方都有介紹,但是其比關係型資料庫的b-tree索引快在哪裡?到底為什麼快呢?

籠統的來說,b-tree索引是為寫入優化的索引結構。當我們不需要支援快速的更新的時候,可以用預先排序等方式換取更小的儲存空間,更快的檢索速度等好處,其代價就是更新慢。要進一步深入的化,還是要看一下Lucene的倒排索引是怎麼構成的。

這裡有好幾個概念。我們來看一個實際的例子,假設有如下的資料:

這裡每一行是一個document。每個document都有一個docid。那麼給這些document建立的倒排索引就是:

年齡:

性別:

可以看到,倒排索引是per field的,一個欄位由一個自己的倒排索引。18,20這些叫做 term,而[1,3]就是posting list。Posting list就是一個int的陣列,儲存了所有符合某個term的文件id。那麼什麼是term dictionary 和 term index?

假設我們有很多個term,比如:

Carla,Sara,Elin,Ada,Patty,Kate,Selena

如果按照這樣的順序排列,找出某個特定的term一定很慢,因為term沒有排序,需要全部過濾一遍才能找出特定的term。排序之後就變成了:

Ada,Carla,Elin,Kate,Patty,Sara,Selena

這樣我們可以用二分查詢的方式,比全遍歷更快地找出目標的term。這個就是 term dictionary。有了term dictionary之後,可以用 logN 次磁碟查詢得到目標。但是磁碟的隨機讀操作仍然是非常昂貴的(一次random access大概需要10ms的時間)。所以儘量少的讀磁碟,有必要把一些資料快取到記憶體裡。但是整個term dictionary本身又太大了,無法完整地放到記憶體裡。於是就有了term index。term index有點像一本字典的大的章節表。比如:

A開頭的term ……………. Xxx頁

C開頭的term ……………. Xxx頁

E開頭的term ……………. Xxx頁

如果所有的term都是英文字元的話,可能這個term index就真的是26個英文字元表構成的了。但是實際的情況是,term未必都是英文字元,term可以是任意的byte陣列。而且26個英文字元也未必是每一個字元都有均等的term,比如x字元開頭的term可能一個都沒有,而s開頭的term又特別多。實際的term index是一棵trie 樹:

例子是一個包含 "A", "to", "tea", "ted", "ten", "i", "in", 和 "inn" 的 trie 樹。這棵樹不會包含所有的term,它包含的是term的一些字首。通過term index可以快速地定位到term dictionary的某個offset,然後從這個位置再往後順序查詢。再加上一些壓縮技術(搜尋 Lucene Finite State Transducers) term index 的尺寸可以只有所有term的尺寸的幾十分之一,使得用記憶體快取整個term index變成可能。整體上來說就是這樣的效果。

現在我們可以回答“為什麼Elasticsearch/Lucene檢索可以比mysql快了。Mysql只有term dictionary這一層,是以b-tree排序的方式儲存在磁碟上的。檢索一個term需要若干次的random access的磁碟操作。而Lucene在term dictionary的基礎上添加了term index來加速檢索,term index以樹的形式快取在記憶體中。從term index查到對應的term dictionary的block位置之後,再去磁碟上找term,大大減少了磁碟的random access次數。

額外值得一提的兩點是:term index在記憶體中是以FST(finite state transducers)的形式儲存的,其特點是非常節省記憶體。Term dictionary在磁碟上是以分block的方式儲存的,一個block內部利用公共字首壓縮,比如都是Ab開頭的單詞就可以把Ab省去。這樣term dictionary可以比b-tree更節約磁碟空間。

如何聯合索引查詢

所以給定查詢過濾條件 age=18 的過程就是先從term index找到18在term dictionary的大概位置,然後再從term dictionary裡精確地找到18這個term,然後得到一個posting list或者一個指向posting list位置的指標。然後再查詢 gender=女 的過程也是類似的。最後得出 age=18 AND gender=女 就是把兩個 posting list 做一個“與”的合併。

這個理論上的“與”合併的操作可不容易。對於mysql來說,如果你給age和gender兩個欄位都建立了索引,查詢的時候只會選擇其中最selective的來用,然後另外一個條件是在遍歷行的過程中在記憶體中計算之後過濾掉。那麼要如何才能聯合使用兩個索引呢?有兩種辦法:

使用skip list資料結構。同時遍歷gender和age的posting list,互相skip;使用bitset資料結構,對gender和age兩個filter分別求出bitset,對兩個bitset做AN操作。PostgreSQL 從 8.4 版本開始支援通過bitmap聯合使用兩個索引,就是利用了bitset資料結構來做到的。當然一些商業的關係型資料庫也支援類似的聯合索引的功能。Elasticsearch支援以上兩種的聯合索引方式,如果查詢的filter快取到了記憶體中(以bitset的形式),那麼合併就是兩個bitset的AND。如果查詢的filter沒有快取,那麼就用skip list的方式去遍歷兩個on disk的posting list。

利用 Skip List 合併:

以上是三個posting list。我們現在需要把它們用AND的關係合併,得出posting list的交集。首先選擇最短的posting list,然後從小到大遍歷。遍歷的過程可以跳過一些元素,比如我們遍歷到綠色的13的時候,就可以跳過藍色的3了,因為3比13要小。

整個過程如下:

Next -> 2

Advance(2) -> 13

Advance(13) -> 13

Already on 13

Advance(13) -> 13 MATCH!!!

Next -> 17

Advance(17) -> 22

Advance(22) -> 98

Advance(98) -> 98

Advance(98) -> 98 MATCH!!!

最後得出的交集是[13,98],所需的時間比完整遍歷三個posting list要快得多。但是前提是每個list需要指出Advance這個操作,快速移動指向的位置。什麼樣的list可以這樣Advance往前做蛙跳?skip list:

從概念上來說,對於一個很長的posting list,比如:

[1,3,13,101,105,108,255,256,257]

我們可以把這個list分成三個block:

[1,3,13] [101,105,108] [255,256,257]

然後可以構建出skip list的第二層:

[1,101,255]

1,101,255分別指向自己對應的block。這樣就可以很快地跨block的移動指向位置了。

Lucene自然會對這個block再次進行壓縮。其壓縮方式叫做Frame Of Reference編碼。示例如下:

考慮到頻繁出現的term(所謂low cardinality的值),比如gender裡的男或者女。如果有1百萬個文件,那麼性別為男的posting list裡就會有50萬個int值。用Frame of Reference編碼進行壓縮可以極大減少磁碟佔用。這個優化對於減少索引尺寸有非常重要的意義。當然mysql b-tree裡也有一個類似的posting list的東西,是未經過這樣壓縮的。

因為這個Frame of Reference的編碼是有解壓縮成本的。利用skip list,除了跳過了遍歷的成本,也跳過了解壓縮這些壓縮過的block的過程,從而節省了cpu。

利用bitset合併

Bitset是一種很直觀的資料結構,對應posting list如:

[1,3,4,7,10]

對應的bitset就是:

[1,0,1,1,0,0,1,0,0,1]

每個文件按照文件id排序對應其中的一個bit。Bitset自身就有壓縮的特點,其用一個byte就可以代表8個文件。所以100萬個文件只需要12.5萬個byte。但是考慮到文件可能有數十億之多,在記憶體裡儲存bitset仍然是很奢侈的事情。而且對於個每一個filter都要消耗一個bitset,比如age=18快取起來的話是一個bitset,18

所以祕訣就在於需要有一個數據結構:

可以很壓縮地儲存上億個bit代表對應的文件是否匹配filter;這個壓縮的bitset仍然可以很快地進行AND和 OR的邏輯操作。Lucene使用的這個資料結構叫做 Roaring Bitmap。

其壓縮的思路其實很簡單。與其儲存100個0,佔用100個bit。還不如儲存0一次,然後宣告這個0重複了100遍。

這兩種合併使用索引的方式都有其用途。Elasticsearch對其效能有詳細的對比(https://www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps)。簡單的結論是:因為Frame of Reference編碼是如此高效,對於簡單的相等條件的過濾快取成純記憶體的bitset還不如需要訪問磁碟的skip list的方式要快。

如何減少文件數?

一種常見的壓縮儲存時間序列的方式是把多個數據點合併成一行。Opentsdb支援海量資料的一個絕招就是定期把很多行資料合併成一行,這個過程叫compaction。類似的vivdcortext使用mysql儲存的時候,也把一分鐘的很多資料點合併儲存到mysql的一行裡以減少行數。

這個過程可以示例如下:

合併之後就變成了:

可以看到,行變成了列了。每一列可以代表這一分鐘內一秒的資料。

Elasticsearch有一個功能可以實現類似的優化效果,那就是Nested Document。我們可以把一段時間的很多個數據點打包儲存到一個父文件裡,變成其巢狀的子文件。示例如下:

{timestamp:12:05:01, idc:sz, value1:10,value2:11}

{timestamp:12:05:02, idc:sz, value1:9,value2:9}

{timestamp:12:05:02, idc:sz, value1:18,value:17}

可以打包成:

{

max_timestamp:12:05:02, min_timestamp: 1205:01, idc:sz,

records: [

{timestamp:12:05:01, value1:10,value2:11}

{timestamp:12:05:02, value1:9,value2:9}

{timestamp:12:05:02, value1:18,value:17}

]

}

這樣可以把資料點公共的維度欄位上移到父文件裡,而不用在每個子文件裡重複儲存,從而減少索引的尺寸。

在儲存的時候,無論父文件還是子文件,對於Lucene來說都是文件,都會有文件Id。但是對於巢狀文件來說,可以儲存起子文件和父文件的文件id是連續的,而且父文件總是最後一個。有這樣一個排序性作為保障,那麼有一個所有父文件的posting list就可以跟蹤所有的父子關係。也可以很容易地在父子文件id之間做轉換。把父子關係也理解為一個filter,那麼查詢時檢索的時候不過是又AND了另外一個filter而已。前面我們已經看到了Elasticsearch可以非常高效地處理多filter的情況,充分利用底層的索引。

使用了巢狀文件之後,對於term的posting list只需要儲存父文件的doc id就可以了,可以比儲存所有的資料點的doc id要少很多。如果我們可以在一個父文件裡塞入50個巢狀文件,那麼posting list可以變成之前的1/50。

如何利用索引和主儲存,是一種兩難的選擇。

選擇不使用索引,只使用主儲存:除非查詢的欄位就是主儲存的排序欄位,否則就需要順序掃描整個主儲存。選擇使用索引,然後用找到的row id去主儲存載入資料:這樣會導致很多碎片化的隨機讀操作。沒有所謂完美的解決方案。MySQL支援索引,一般索引檢索出來的行數也就是在1~100條之間。如果索引檢索出來很多行,很有可能MySQL會選擇不使用索引而直接掃描主儲存,這就是因為用row id去主儲存裡讀取行的內容是碎片化的隨機讀操作,這在普通磁碟上很慢。

Opentsdb是另外一個極端,它完全沒有索引,只有主儲存。使用Opentsdb可以按照主儲存的排序順序快速地掃描很多條記錄。但是訪問的不是按主儲存的排序順序仍然要面對隨機讀的問題。

Elasticsearch/Lucene的解決辦法是讓主儲存的隨機讀操作變得很快,從而可以充分利用索引,而不用懼怕從主儲存裡隨機讀載入幾百萬行帶來的代價。

Opentsdb 的弱點

Opentsdb沒有索引,主儲存是Hbase。所有的資料點按照時間順序排列儲存在Hbase中。Hbase是一種支援排序的儲存引擎,其排序的方式是根據每個row的rowkey(就是關係資料庫裡的主鍵的概念)。MySQL儲存時間序列的最佳實踐是利用MySQL的Innodb的clustered index特性,使用它去模仿類似Hbase按rowkey排序的效果。所以Opentsdb的弱點也基本適用於MySQL。Opentsdb的rowkey的設計大致如下:

[metric_name][timestamp][tags]

舉例而言:

Proc.load_avg.1m 12:05:00 ip=10.0.0.1

Proc.load_avg.1m 12:05:00 ip=10.0.0.2

Proc.load_avg.1m 12:05:01 ip=10.0.0.1

Proc.load_avg.1m 12:05:01 ip=10.0.0.2

Proc.load_avg.5m 12:05:00 ip=10.0.0.1

Proc.load_avg:5m 12:05:00 ip=10.0.0.2

也就是行是先按照metric_name排序,再按照timestamp排序,再按照tags來排序。

對於這樣的rowkey設計,獲取一個metric在一個時間範圍內的所有資料是很快的,比如Proc.load_avg.1m在12:05到12:10之間的所有資料。先找到Proc.load_avg.1m 12:05:00的行號,然後按順序掃描就可以了。

但是以下兩種情況就麻煩了。

獲取12:05 到 12:10 所有 Proc.load_avg.* 的資料,如果預先知道所有的metric name包括Proc.load_avg.1m,Proc.load_avg.5m,Proc.load_avg.15m。這樣會導致很多的隨機讀。如果不預先知道所有的metric name,就無法知道Proc.load_avg.*代表了什麼。獲取指定ip的資料。因為ip是做為tags儲存的。即便是訪問一個ip的資料,也要把所有其他的ip資料讀取出來再過濾掉。如果ip總數有十多萬個,那麼查詢的效率也會非常低。為了讓這樣的查詢變得更快,需要把ip編碼到metric_name裡去。比如ip.10.0.0.1.Proc.load_avg.1m 這樣。所以結論是,不用索引是不行的。如果希望支援任意條件的組合查詢,只有主儲存的排序是無法對所有查詢條件進行優化的。但是如果查詢條件是固定的一種,那麼可以像Opentsdb這樣只有一個主儲存,做針對性的優化。

DocValues為什麼快?

DocValues是一種按列組織的儲存格式,這種儲存方式降低了隨機讀的成本。傳統的按行儲存是這樣的:

1和2代表的是docid。顏色代表的是不同的欄位。

改成按列儲存是這樣的:

按列儲存的話會把一個檔案分成多個檔案,每個列一個。對於每個檔案,都是按照docid排序的。這樣一來,只要知道docid,就可以計算出這個docid在這個檔案裡的偏移量。也就是對於每個docid需要一次隨機讀操作。

那麼這種排列是如何讓隨機讀更快的呢?祕密在於Lucene底層讀取檔案的方式是基於memory mapped byte buffer的,也就是mmap。這種檔案訪問的方式是由作業系統去快取這個檔案到記憶體裡。這樣在記憶體足夠的情況下,訪問檔案就相當於訪問記憶體。那麼隨機讀操作也就不再是磁碟操作了,而是對記憶體的隨機讀。

那麼為什麼按行儲存不能用mmap的方式呢?因為按行儲存的方式一個檔案裡包含了很多列的資料,這個檔案尺寸往往很大,超過了作業系統的檔案快取的大小。而按列儲存的方式把不同列分成了很多檔案,可以只快取用到的那些列,而不讓很少使用的列資料浪費記憶體。

按列儲存之後,一個列的資料和前面的posting list就差不多了。很多應用在posting list上的壓縮技術也可以應用到DocValues上。這不但減少了檔案尺寸,而且提高資料載入的速度。因為我們知道從磁碟到記憶體的頻寬是很小的,普通磁碟也就每秒100MB的讀速度。利用壓縮,我們可以把資料以壓縮的方式讀取出來,然後在記憶體裡再進行解壓,從而獲得比讀取原始資料更高的效率。

如果記憶體不夠是不是會使得隨機讀的速度變慢?肯定會的。但是mmap是作業系統實現的API,其內部有預讀取機制。如果讀取offset為100的檔案位置,預設會把後面16k的檔案內容都預讀取出來都快取在記憶體裡。因為DocValues是隻讀,而且順序排序儲存的。相比b-tree等儲存結構,在磁碟上沒有空洞和碎片。而隨機讀的時候也是按照DocId排序的。所以如果讀取的DocId是緊密相連的,實際上也相當於把隨機讀變成了順序讀了。Random_read(100), Random_read(101), Random_read(102)就相當於Scan(100~102)了。

分散式計算

分散式聚合如何做得快?Elasticsearch/Lucene從最底層就支援資料分片,查詢的時候可以自動把不同分片的查詢結果合併起來。Elasticsearch的document都有一個uid,預設策略是按照uid 的 hash把文件進行分片。

一個Elasticsearch Index相當於一個MySQL裡的表,不同Index的資料是物理上隔離開來的。Elasticsearch的Index會分成多個Shard儲存,一部分Shard是Replica備份。一個Shard是一份本地的儲存(一個本地磁碟上的目錄),也就是一個Lucene的Index。不同的Shard可能會被分配到不同的主機節點上。一個Lucene Index會儲存很多的doc,為了好管理,Lucene把Index再拆成了Segment儲存(子目錄)。Segment內的doc數量上限是1的31次方,這樣doc id就只需要一個int就可以儲存。Segment對應了一些列檔案儲存索引(倒排表等)和主儲存(DocValues等),這些檔案內部又分為小的Block進行壓縮。

時間序列資料一般按照日期分成多個Elasticsearch Index來儲存,比如logstash-2014.08.02。查詢的時候可以指定多個Elasticsearch Index作為查詢的範圍,也可以用logstash-*做模糊匹配。

美妙之處在於,雖然資料被拆得七零八落的,在查詢聚合的時候甚至需要分為兩個階段完成。但是對於終端使用者來說,使用起來就好像是一個數據庫表一樣。所有的合併查詢的細節都是隱藏起來的。

對於聚合查詢,其處理是分兩階段完成的:

Shard本地的Lucene Index平行計算出局部的聚合結果;收到所有的Shard的區域性聚合結果,聚合出最終的聚合結果。這種兩階段聚合的架構使得每個shard不用把原資料返回,而只用返回資料量小得多的聚合結果。相比Opentsdb這樣的資料庫設計更合理。Opentsdb其聚合只在最終節點處完成,所有的分片資料要匯聚到一個地方進行計算,這樣帶來大量的網路頻寬消耗。所以Influxdb等更新的時間序列資料庫選擇把分散式計算模組和儲存引擎進行同機部署,以減少網路頻寬的影響。

除此之外Elasticsearch還有另外一個減少聚合過程中網路傳輸量的優化,那就是Hyperloglog演算法。在計算unique visitor(uv)這樣的場景下,經常需要按使用者id去重之後統計人數。最簡單的實現是用一個hashset儲存這些使用者id。但是用set儲存所有的使用者id做去重需要消耗大量的記憶體,同時分散式聚合的時候也要消耗大量的網路頻寬。Hyperloglog演算法以一定的誤差做為代價,可以用很小的資料量儲存這個set,從而減少網路傳輸消耗。

為什麼時間序列需要更復雜的聚合?

關係型資料庫支援一些很複雜的聚合查詢邏輯,比如:

Join兩張表;Group by之後用Having再對聚合結果進行過濾;用子查詢對聚合結果進行二次聚合。在使用時間序列資料庫的時候,我們經常會懷念這些SQL的查詢能力。在時間序列裡有一個特別常見的需求就是降頻和降維。舉例如下:

12:05:05 湖南 81

12:05:07 江西 30

12:05:11 湖南 80

12:05:12 江西 32

12:05:16 湖南 80

12:05:16 江西 30

按1分鐘頻率進行max的降頻操作得出的結果是:

12:05 湖南 81

12:05 江西 32

這種按max進行降頻的最常見的場景是取樣點的歸一化。不同的採集器取樣的時間點是不同的,為了避免漏點也會加大采樣率。這樣就可能導致一分鐘內取樣多次,而且取樣點的時間都不對齊。在查詢的時候按max進行降頻可以得出一個統一時間點的資料。

按sum進行降維的結果是:

12:05 113

經常我們需要捨棄掉某些維度進行一個加和的統計。這個統計需要在時間點對齊之後再進行計算。這就導致一個查詢需要做兩次,上面的例子裡:

先按1分鐘,用max做降頻;再去掉省份維度,用sum做降維。如果僅僅能做一次聚合,要麼用sum做聚合,要麼用max做聚合。無法滿足業務邏輯的需求。為了避免在一個查詢裡做兩次聚合,大部分的時間序列資料庫都要求資料在入庫的時候已經是整點整分的。這就要求資料不能直接從採集點直接入庫,而要經過一個實時計算管道進行處理。如果能夠在查詢的時候同時完成降頻和降維,那就可以帶來一些使用上的便利。

這個功能看似簡單,其實非常難以實現。很多所謂的支援大資料的資料庫都只支援簡單的一次聚合操作。Elasticsearch 將要釋出的 2.0 版本的最重量級的新特性是Pipeline Aggregation,它支援資料在聚合之後再做聚合。類似SQL的子查詢和Having等功能都將被支援。

Reference:科技日報

看更多!請加入我們的粉絲團

轉載請附文章網址

不可錯過的話題