使用Apache Kafka和MongoDB串流时间序列数据

365 阅读5分钟

使用Apache Kafka和MongoDB进行时间序列数据流处理

有一件事是全世界都同意的,那就是时间的概念。许多应用都是基于时间的。考虑一下太阳能场发电、股票交易和健康监测。这些只是产生和使用包含关键时间成分的数据的大量应用中的几个。一般来说,时间序列数据应用重在插入,很少执行更新,更不可能删除数据。这些应用产生了巨大的数据量,需要一个强大的数据平台来有效地管理和查询数据。使用MongoDB,你可以轻松地:

  • 使用MongoDB查询语言和窗口函数对数据进行预先汇总

  • 用MongoDB时间序列集合优化存储大量的时间序列数据

  • 使用MongoDB Atlas Online Archive将数据归档到具有成本效益的存储中

由于Apache Kafka的可扩展性,它经常被用作数据的摄入点。通过使用MongoDB Connector for Apache Kafka和Apache Kafka Connect服务,很容易在Kafka主题和MongoDB集群之间传输数据。从MongoDB Connector for Apache Kafka的1.6版本开始,你可以配置kafka主题数据直接写入MongoDB的时间序列集合。这种配置发生在汇中。

在水槽中配置时间序列集合

使用MongoDB,应用程序不需要在开始写入数据之前创建数据库和集合。这些对象会在数据首次到达MongoDB时自动创建。然而,在开始写入数据之前,需要先创建一个时间序列集合类型。为了方便从Kafka将时间序列数据摄入MongoDB,这些集合选项被暴露为sink参数,如果时间序列集合还不存在,则由连接器创建。一些新的参数被定义如下。

timeseries.timefield用于时间的顶层字段的名称。

timeseries.expire.after.seconds这个可选的字段决定了数据在被自动删除之前在MongoDB中的时间量。省略此字段意味着数据将不会被自动删除。如果你熟悉MongoDB中的TTL索引,设置这个字段可以提供一个类似的行为。

timeseries.timefield.auto.convert这个可选的字段告诉连接器将字段中的数据转换成BSON Date格式。支持的格式包括整数、长和字符串。

有关新的时间序列参数的完整列表,请查看MongoDB Sink连接器的在线文档。当数据存储在时间序列集合中时,MongoDB在幕后优化了数据的存储和桶化。与普通集合中典型的每个数据点一个文档的数据结构相比,这节省了大量的存储空间。你还可以探索MongoDB查询语言中许多新的时间和窗口功能。例如,考虑这个样本文档结构:

{
  tx_time: 2021-06-30T15:47:31.000Z,
  _id: '60dc921372f0f39e2cd6cba5',
  company_name: 'SILKY CORNERSTONE LLC',
  price: 94.0999984741211,
  company_symbol: 'SCL'
}

你可以使用新的$setWindowFields pipeline来定义文档的窗口来执行操作,然后执行排名、累积总数和其他复杂时间序列数据的分析。例如,使用教程中生成的数据,让我们对数据确定滚动平均数,如下所示:

db.StockDataTS.aggregate(
[
       {
            $match: {company_symbol: 'SCL'}
       },
       {
            $setWindowFields:
                  {
                        partitionBy: '$company_name',
                        sortBy: { 'tx_time': 1 },
                        output:
                               {
                                        averagePrice:
                                        {
                                              $avg: "$price",
                                              window:
                                                    {
                                                    Documents:
                                                         [ "unbounded", "current" ]
                                                    }
                                        }
                               }
                  }
        }
])

结果集的一个样本如下:

{
    tx_time: 2021-06-30T15:47:45.000Z,
    _id: '60dc922172f0f39e2cd6cbeb',
    company_name: 'SILKY CORNERSTONE LLC',
    price: 94.06999969482422,
    company_symbol: 'SCL',
    averagePrice: 94.1346669514974
  },
  {
    tx_time: 2021-06-30T15:47:47.000Z,
    _id: '60dc922372f0f39e2cd6cbf0',
    company_name: 'SILKY CORNERSTONE LLC',
    price: 94.1500015258789,
    company_symbol: 'SCL',
    averagePrice: 94.13562536239624
  },
  {
    tx_time: 2021-06-30T15:47:48.000Z,
    _id: '60dc922472f0f39e2cd6cbf5',
    company_name: 'SILKY CORNERSTONE LLC',
    price: 94.0999984741211,
    company_symbol: 'SCL',
    averagePrice: 94.13352966308594
  }

注意额外的 "averagePrice "字段现在是用滚动平均数填充的。关于MongoDB中时间序列集合的更多信息,请查看在线文档

迁移现有的集合

要将现有的MongoDB集合转换为时间序列集合,你可以使用MongoDB Connector for Apache Kafka。只需将源连接配置到你现有的集合,并通过使用 "timeseries.timefield "参数将汇连接器配置为写到MongoDB时间序列集合。你可以通过将 "copy.existing "参数设置为 "true "来配置源连接器以复制现有数据。这将为源中的所有现有文档创建插入事件。任何在复制过程中被插入的文档都将在复制过程结束后被插入。虽然并不总是可能,但建议在复制过程中暂停对源数据的写入。要看它何时完成,你可以查看日志中的消息,"完成了从集合中复制现有数据"。

例如,考虑一个具有这种结构的源文件:

{
	company_symbol: (STRING),
	company_name: (STRING),
 	price: (DECIMAL),
	tx_time: (STRING)
}

对于MongoDB时间序列集合的初始版本,代表时间的字段需要被存储为Date。在我们的例子中,我们使用一个字符串来展示连接器自动从字符串转换为日期的能力。如果你选择在连接器之外执行转换,你可以在Kafka Connect中使用单一消息转换,在Sink将字符串转换为日期。然而,某些SMT(如Timestampconverter)需要为Kafka主题中的数据定义模式,以便工作。这可能会给配置增加一些复杂性。你可以使用新的timeseries.timefield.auto.convert和timeseries.timefield.auto.convert.date.format选项自动转换为Dates,而不是使用SMT。

下面是一个源配置的示例,它将从StockData集合中复制所有的现有数据,然后继续推送数据变化到stockdata.Stocks.StockData主题:

{"name": "mongo-source-stockdata",
        "config": {
          "tasks.max":"1",
          "connector.class":"com.mongodb.kafka.connect.MongoSourceConnector",
          "key.converter":"org.apache.kafka.connect.storage.StringConverter",
          "value.converter":"org.apache.kafka.connect.json.JsonConverter",
          "publish.full.document.only": true,
          "connection.uri":(MONGODB SOURCE CONNECTION STRING),
          "topic.prefix":"stockdata",
          "database":"Stocks",
          "collection":"StockData",
          "copy.existing":"true"
     }}

这是一个样本配置,用于将stockdata.Stocks.StockData主题中的数据写入MongoDB的时间序列集合:

 {"name": "mongo-sink-stockdata",
        "config": {
          "connector.class":"com.mongodb.kafka.connect.MongoSinkConnector",
          "tasks.max":"1",
          "topics":"stockdata.Stocks.StockData",
          "connection.uri":(MONGODB SINK CONNECTION STRING),
          "database":"Stocks",
          "collection":"StockDataMigrate",
          "key.converter":"org.apache.kafka.connect.storage.StringConverter",
          "value.converter":"org.apache.kafka.connect.json.JsonConverter",
          "timeseries.timefield":"tx_time",
          "timeseries.timefield.auto.convert":"true",
          "timeseries.timefield.auto.convert.date.format":"yyyy-MM-dd'T'HH:mm:ss'Z'"
          
     }}

在这个水槽示例中,连接器将把 "tx_time "字段中的数据转换为Date,并将其解析为yyyy-MM-ddTHH:mm:ssZ的字符串格式(例如:'2021-07-06T12:25:45Z')

注意,在时间序列集合的初始版本中,只支持插入到时间序列集合中。在源文件上更新或删除文件将不会传播到目的地。另外,你不能在这种情况下使用MongoDB CDC处理程序,因为该处理程序使用ReplaceOne,这是一种更新命令。这些都是MongoDB中最初发布的时间序列的限制,当你看到这篇文章时,可能已经不重要了。请查看在线文档以了解最新信息。

Apache Kafka的MongoDB连接器1.6版可以从GitHub下载。本周晚些时候,在Confluent Hub上可以看到它。