Cassandra 数据建模物联网最佳实践

Cassandra Data modeling IoT best practices

我是 Cassandra 的新手,我正在尝试了解如何为 IoT 传感器设计 tables。

想法是拥有多个设备,每个设备都连接有多个传感器,定期发送数据(每个设备每天每个传感器最多发送大约 200000 个值)

我希望能够或多或少地实时查询特定传感器和设备列表的传感器的最新值。此外,设备并不总是发送数据,并且可能会长时间停机。

经过大量阅读,我想到了这样的东西

CREATE TABLE "sensor_data" (
    deviceid TEXT,
    sensorid TEXT,
    ts timestamp,
    value TEXT,
    PRIMARY KEY ((deviceid, sensorid), ts)
) WITH CLUSTERING ORDER BY (ts DESC);

这背后的想法是对每个设备和传感器执行一个查询,例如

Select deviceid, sensorid, ts, value where deviceid = "device1" and sensorid = "temperature" limit 1

并且 运行 这适用于每个设备和传感器。这不是 return 全部的一个查询(这将是理想的)但似乎足够快 运行 对于一些设备可能多达 100 个左右的传感器(有可能并行查询) .

然而,根据我目前所读的内容,我知道这会为我的行提供很多列,并且在长期存储和 Cassandra 限制方面可能会很复杂。

我在想,也许像这样向 table 添加类似日期的内容(如某些博客和指南中所见)可能是个好主意

CREATE TABLE "sensor_data" (
    deviceid TEXT,
    sensorid TEXT,
    date TEXT
    ts timestamp,
    value TEXT,
    PRIMARY KEY ((deviceid, sensorid, date), ts)
) WITH CLUSTERING ORDER BY (ts DESC);

然后查询like

Select deviceid, sensorid, date, ts, value where deviceid = "device1" and sensorid = "temperature" and date = "2018-11-14" limit 1

这有意义吗?感觉它可能会缓解存储问题并允许将来更轻松地归档旧数据,但是如果该设备停机一天或更长时间,我该如何查询特定传感器和设备的最新值?真的要查1天吗,没查到就查前一天等等(可能只限最近几天)?

在 Cassandra 中是否有更好的方法来处理这个问题,或者我的方向是否正确?

您将 运行 遇到的部分问题是每个传感器每天将有 20 万个读数。通常,您希望将每个分区保持在 100k rows 之下。因此,您的第二个想法(将日期作为 PK 的一部分)可能存在性能问题。

您真正想要做的就是我们所说的'bucketing';如何将事物组合在一起以便查询可用且高效。

要真正帮助解决这个问题,我们需要了解更多信息:

  • 你有多少台设备?这个数字会增长还是有限?
  • 用简单的英语,您尝试回答的查询示例是什么?

根据您的回答(如下)将其合并到答案中:

好的,这是一个潜在的想法...

我们确实关心分桶,但试图保持分区中 100k/分区最佳行。

您需要两个 table:

  1. 查找table
  2. 传感器table

查找 table 类似于:

CREATE TABLE lookup-table (
deviceid TEXT,
sensor-map MAP,
PRIMARY KEY (deviceid)
);
  • deviceid是每个设备的唯一ID
  • sensor-map 是给定设备具有的 JSON map 传感器以及该特定传感器的相应唯一 ID(例如 {温度:183439,湿度:84543292,其他-传感器:废话})
  • 这样每个设备都有一个可用的传感器映射
  • 示例查询为:SELECT * FROM lookup-table WHERE deviceid = 1234;
  • 另一种方法是为每种类型的传感器设置单独的列,并将每个传感器的唯一 ID 作为值

传感器 table 看起来像:

CREATE TABLE sensor_data (
sensorid TEXT,
sensor_value (whatever data type fits what you need),
ts TIMESTAMP,
reading_date date,
time_bucket int,
PRIMARY KEY ((reading_date, sensorid, time_bucket), ts)
) WITH CLUSTERING ORDER BY (ts DESC);
  1. 由于每个传感器将获得 200k readings/day 并且我们希望将每个分区保持在 100k 行以下,这意味着我们希望每天为每个传感器执行两个分区
  2. 你怎么能桶?你应该分两次 parts:you 每天一次;每个传感器每天都会获得一个新分区 (reading_date) 并将每天分成两部分(由于您期望的读数量);上午或下午; AM 等于桶 1,PM 等于桶 2。或者使用 24 小时时间,其中 0-1200 等于 1,1300-2399 等于 2
  3. 在您的申请中提供具体的 sensoridtime_bucket 将来自您实际请求的时间 查询(例如,如果时间是 1135 小时,则 time_bucket = 1)和 reading_date 将来自您查询的实际日期
  4. 由于您正在与 ts DESC 进行聚类,因此它将检索 给定 sensorid 的最新阅读。所以它看起来像 SELECT * from sensor_data WHERE reading_date = 12/31/2017 AND sensorid = 1234 AND time_bucket = 1 LIMIT 1;
  5. 通过将 ts 维护为聚类列,您将能够保留给定传感器的所有读数; none 将被覆盖

重要提示:如果传感器读数在一天 24 小时内均匀分布,则此方法效果很好。然而,如果你早上阅读量很大,而下午根本不阅读,那么这就不是一个偶数,我们将不得不想出另一种方法来存储。但是,我认为你明白发生了什么。

查询:

  • 将有一个查询来检索设备具有的所有 sensorid;一旦你有了这些 sensorid,你就可以将其用于下一步
  • 对于每个 sensorid
  • ,每个 sensor_value 将有 n 个查询
  • 由于我们正在分桶(通过 time_bucket),您应该在所有分区中均匀分布

最后:给定值给我最新的sensorid 要做到这一点,有几种不同的方法...

  • 运行 Spark 作业:为此,您必须将数据提升并转移到 运行 Spark 查询
  • 使用 DataStax Enterprise:借助 DSE,您拥有一个基于 Spark 的集成分析组件,因此您可以 运行 Spark 作业,而无需管理单独的 Spark 集群。披露:我在那里工作,顺便说一句
  • 创建一个额外的 Cassandra (C*) table 并进行一些并行写入

对于额外的 C* table:

CREATE TABLE sensor_by_value (
sensor-value INT,
ts TIMESTAMP,
sensorid TEXT,
reading_date DATE,
time_bucket INT,
PRIMARY KEY ((sensor-value, reading_date), ts)
) WITH CLUSTERING ORDER BY (ts DESC);

你肯定要在这里做一些时间桶:

  • 记住,我们不希望每个分区超过 100k 行
  • 您必须了解可能的值(范围)
  • 每次阅读的频率
  • 如果您有 100 个设备、100 个传感器,并且每个传感器每天最多读取 200k,那么您每天有可能读取多达 2B 个传感器读数...
  • 通常,我让我的客户做的是 运行 对他们的数据进行一些分析以了解这些信息,这样您就可以确保对其进行解释
  • 你要倒多少取决于频率
  • 祝你好运! :-)

最后提示

研究压缩策略:特别是 time window compaction strategy (TWCS) 并添加 default_time_to_live

  • 您的数据在初始插入后似乎不可靠table

  • TWCS 将在您根据需要

  • 时间window 对其进行微调时大大降低压缩的操作开销
  • A default_ttl 也将有助于在您不再需要数据后删除数据的操作开销。

这个答案 and/or 是否满足您要回答的问题?如果没有,请告诉我,我们可以迭代。

要了解所有这些内容,请转到 DataStax Academy 进行大量免费培训。数据建模 (DS 220) 是一门很棒的课程!