使用MongoDB儲存時間序列資料 - DAC

banq發表於2019-11-24

瞭解如何使用Java + Spring示例在MongoDB中最佳化時序資料的儲存。

什麼是時間序列資料?
引用維基百科,“ 時間序列是按時間順序索引(或列出或繪製)的一系列資料點 ”。但這實際上是什麼意思?讓我們舉個例子。
考慮交通工程公司的情況。在特定的用例中,該公司需要收集橫穿特定路口的確切數量的汽車的資料,以便管理交通燈演算法。透過交叉路口的每輛汽車都將被報告回系統。這種事件就是我們所說的資料時間點。經過交叉路口的多輛汽車就是我們所說的資料“系列”。
時間序列資料可能以不規則的速率生成-在我們的示例中,它僅在汽車經過時才出現-或可以以固定的時間間隔捕獲-例如每秒的裝置測量值。

Mongo資料讀取
假設我們要每隔1秒收集一次裝置溫度。在典型的MongoDB文件中儲存該資訊看起來像這樣:

{
    "_id" : ObjectId(),
    "temperature" : 90.50,
    "deviceId" : "1",
    "date" : ISODate("2019–11–10T00:00:01Z")
},
{
    "_id" : ObjectId(),
    "temperature" : 92.50,
    "deviceId" : "1",
    "date" : ISODate("2019–11–10T00:00:02Z")
}

每分鐘產生60個文件,每小時產生3600個檔案,每天產生86400個文件,這還只是一臺裝置!這種方法有明顯的缺陷,並且對效能和磁碟使用率有重大影響。
必須有更好的解決方案…

Mongo時間序列,又稱基於大小的儲存桶
MongoDB允許我們建立一個文件來儲存多個連續的資料讀取。在我們裝置的溫度收集情況下,此類檔案要求:

  • id —文件的ID(MongoDB的ObjectId)
  • deviceId —查詢樣品時文件分組依據的元素
  • bucketSize -檔案中的最大樣本量
  • date—樣品的日期;我們可以將其儲存在此處以簡化聚合
  • first —在儲存桶中讀取的最舊資料的時間戳
  • last —儲存桶中讀取的最新資料的時間戳
  • samples —資料容器

我們的最終時間序列檔案將類似於以下檔案:

{
    _id: ObjectId(),
    deviceid: 1,
    date: ISODate("2019-11-10"),
    bucketSize: 4,
    first: 1573833152,
    last: 1573833155,
    samples : [
       { val: 10, time: 1573833152},
       { val: 15, time : 1573833153},
       { val: 14, time: 1573833154},
       { val: 20, time : 1573833155}
   ]
}

每個新的從裝置讀取得數值都將附加到這個文件,直到儲存桶大小達到1000個樣本的閾值(請參閱:bucketSize值)。upsert:true一旦達到閾值,將透過該子句建立一個新文件。
在upsert過程中,我們將sample物件推送至samples,first將其更新為最小值,last最大值,並bucketSize為每個新條目增加1。

Java和Spring中基於大小的儲存
好了,足夠多的理論,讓我們現在付諸實踐。我們將使用Spring的MongoTemplate將入站溫度讀取插入MongoDB。讓我們實現上面給出的示例。
我們的DeviceTemperature模型將如下所示:

@lombok.Data
class DeviceTemperature {
   private Integer deviceId;
   private Integer temperature;
   private Long timestamp;
   private LocalDate date;
}
DeviceTemperature deviceTemperature = new DeviceTemperature();
deviceTemperature.setDeviceId(1);
deviceTemperature.setTemperature(12);
deviceTemperature.setTimestamp(1573833152L);
deviceTemperature.setDate(LocalDate.parse(“2019–11–10”);


建立好模型後,我們需要建立criteria和query:

Criteria criteria = Criteria.where("deviceId").is(deviceTemperature.getDeviceId())
    .and("bucketSize").lt(1000)
    .and("date").is(deviceTemperature.getDate());
Query query = Query.query(criteria);


上面的程式碼建立了一個查詢,其中包含用於上載特定MongoDB文件的條件。

現在建立一個update命令:

Integer deviceId = deviceTemperature.getDeviceId();
Update update = new Update()
                   .push("samples", deviceTemperature)
                   .inc("bucketSize", deviceId)
                   .min("first", deviceTemperature.getTimestamp())
                   .max("last", deviceTemperature.getTimestamp());


最後呼叫mongoTemplate 實現upset資料:

mongoTemplate.upsert(query, update, DeviceTemperature.class)


這就是使用Java和Spring以時間序列的方式儲存資料所需的一切。
 

相關文章