详解物联网的五种数据模型
admin
2023-09-17 14:24:07
0

译者 | 李睿

审校 | 孙淑娟

Apache Cassandra是大规模管理物联网和时间序列数据的一个可靠选择。在Cassandra中存储、查询和分析物联网设备生成的时间序列的最流行用例已经得到很好的理解和记录。在通常情况下,时间序列是根据其源物联网设备存储和查询的。但是,还有另一类物联网应用程序需要快速访问由一组物联网设备基于已知状态生成的最新数据。此类应用程序需要回答的问题是:哪些物联网设备或传感器当前正在报告特定状态?本文将重点关注这个问题,并提供五种可能的数据建模解决方案,以便在Cassandra中有效地回答这个问题。

1、介绍

物联网正在生成大量需要存储、查询和分析的时间序列数据。Apache Cassandra是这项任务的好选择:不仅因为它的速度、可靠性和可扩展性,还因为它的内部数据模型内置了对时间排序数据的支持。

在Cassandra中,时间序列通常由源(例如物联网设备或传感器)或主题(例如参数或指标)存储和检索。有许多很好的资源非常详细地介绍了这个主题,包括这个会议演示视频,以及用于传感器数据和时间序列的即用型Cassandra数据模型。

本文研究了一些相关的物联网用例,它们需要管理来自许多物联网设备的最新数据的快照。此外,需要根据物联网设备报告的特定状态来查询或过滤这样的快照。换句话说,应该能够在Cassandra中快速回答这个问题:哪些物联网设备当前正在报告特定状态?对于许多现实生活中的用例,这个问题听起来更像是:

  • 智能家居中哪些灯是打开(关闭)的?
  • 停车场中当前有哪些停车位被占用(空置)?
  • 当前在特定位置附近有哪些车辆可用(不可用)?
  • 当前在某个区域触发(激活或禁用)哪些安全警报?
  • 建筑物中当前打开(关闭、锁定、解锁)哪些门?
  • 哪些火灾探测传感器当前报告传感器网络中的异常(正常待机、错误)状态?

本文章更加正式地定义了这些问题,并通过示例CQL实现提出了五个实用的解决方案。

2、问题的定义

给定一组物联网设备或传感器,它们生成包含时间戳、数据点和状态的按时间顺序排列的事件序列,查找所有物联网设备报告的具有已知状态的最新事件。这个问题的三个关键组成部分如下所示:

  • 输入由物联网设备生成的时间序列组成。时间序列通常存储在一个或多个Cassandra表中。
  • 中间视图仅是物联网设备报告的最新事件的快照。可以单独显式存储最新事件,也可以根据输入动态计算它们。
  • 最终结果是所有具有已知状态的最新事件。具有相同状态的最新事件应该存储在一起或易于计算。

3、基于状态管理最新的物联网事件

以下确定了基于状态管理最新物联网事件的几个挑战:

  • 最新事件的快照不断发展。可能需要额外的工作来增量捕获任何更改。
  • 事件发生的频率通常是不可预测的。仅基于事件的时间戳组件可能难以对事件进行分区和组织。
  • 一个状态通常只能采用几个唯一值。基于低基数列对数据进行分区和索引可能会导致大分区。

使用以下运行示例作为起点。表events_by_device是输入。这张具有多行分区的表旨在存储时间序列,这样每个分区对应一个设备,分区中的行表示具有时间戳、状态和值的事件。每个分区中的事件始终按其时间戳降序排序。该表实际上为每个分区存储一个时间序列。将五个事件插入表中并检索一个设备的时间序列。此外,在第二个查询中,演示可以动态计算所有设备的所有最新事件。需要注意的是不应该依赖这个查询来解决问题:它可能会变得代价高昂,因为它访问表中的每个分区。

模式:

CQL

1 -- All events by device2 3 CREATE TABLE events_by_device (4 5  device_id  UUID,6 7  timestamp  TIMESTAMP,89   state      TEXT,1011 value      TEXT,1213 PRIMARY KEY((device_id), timestamp)1415 ) WITH CLUSTERING ORDER BY (timestamp DESC);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

数据:

CQL

1 -- Event 1-123 INSERT INTO events_by_device45 (device_id, timestamp, state, value)67 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,89    '2021-01-01 01:11:11', 'on', 'event 1-1');1011 -- Event 1-21213 INSERT INTO events_by_device1415  (device_id, timestamp, state, value)1617 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,1819  '2021-01-01 02:22:22', 'off', 'event 1-2');2021 -- Event 1-32223  INSERT INTO events_by_device2425   (device_id, timestamp, state, value)2627  VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,2829    '2021-01-01 03:33:33', 'on', 'event 1-3');3031 -- Event 2-13233 INSERT INTO events_by_device3435  (device_id, timestamp, state, value)3637  VALUES (22222222-aaaa-bbbb-cccc-12345678abcd,3839    '2021-02-02 01:11:11', 'off', 'event 2-1');4041 -- Event 3-14243 INSERT INTO events_by_device4445   (device_id, timestamp, state, value)4647 VALUES (33333333-aaaa-bbbb-cccc-12345678abcd,4849 '2021-03-03 01:11:11', 'off', 'event 3-1');1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.

查询:

CQL

1-- Find all events for a device23 SELECT device_id, timestamp, state, value45 FROM   events_by_device67 WHERE  device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;8910device_id                     | timestamp                  | state | value1112--------------------------------------+---------------------------------+-------+-----------1314  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 |    on | event 1-31516  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 02:22:22.000000+0000 |   off | event 1-21718  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 01:11:11.000000+0000 |    on | event 1-1192021-- Find the latest events for all devices2223 SELECT device_id, timestamp, state, value2425 FROM   events_by_device2627 PER PARTITION LIMIT 1;282930  device_id                 | timestamp               | state | value3132--------------------------------------+---------------------------------+-------+-----------3334 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 |   off | event 3-13536 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 |   off | event 2-13738  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 |    on | event 1-31.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.

查询:

CQL

1-- Find all events for a device23 SELECT device_id, timestamp, state, value45 FROM   events_by_device67 WHERE  device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;8910device_id                 | timestamp                       | state | value1112--------------------------------------+---------------------------------+-------+-----------1314  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 |    on | event 1-31516  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 02:22:22.000000+0000 |   off | event 1-21718  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 01:11:11.000000+0000 |    on | event 1-1192021-- Find the latest events for all devices2223 SELECT device_id, timestamp, state, value2425 FROM   events_by_device2627 PER PARTITION LIMIT 1;282930  device_id                 | timestamp                       | state | value3132--------------------------------------+---------------------------------+-------+-----------3334 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 |   off | event 3-13536 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 |   off | event 2-13738  11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 |    on | event 1-31.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.

需要注意,可以假设每个设备的事件数不超过10万件。否则,可能不得不通过在其分区键定义中引入另一列来进一步拆分表events_by_device中的分区。由于这对于本文中解决的问题并不重要,所以尽量保持简单。

鉴于问题定义和物联网事件的运行CQL示例,在此准备描述具有不同特征的五种解决方案。

4、解决方案一:物化视图

第一个解决方案需要一个新表和一个物化视图。表latest_events_by_device是一个单行分区表,其中每个分区对应一个设备,每一行对应最新的已知事件。此表的目的是仅获取物联网设备报告的最新事件的快照。该表也是物化视图latest_events_by_state的基表,可以使用状态查询最新事件。

需要注意,完全相同的数据被插入到表events_by_device和latest_events_by_device中。对于后者,插入变为更新插入,将行更新为最新事件。

模式:

CQL

1 -- Latest known events by device23 CREATE TABLE latest_events_by_device (45 device_id  UUID,67 timestamp  TIMESTAMP,89  state      TEXT,1011 value      TEXT,1213   PRIMARY KEY((device_id))1415 );161718 -- Latest events by state1920 CREATE MATERIALIZED VIEW latest_events_by_state AS2122 SELECT * FROM latest_events_by_device2324 WHERE state IS NOT NULL AND device_id IS NOT NULL2526 PRIMARY KEY ((state), device_id);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.

数据:

CQL

1-- Event 1-123 INSERT INTO latest_events_by_device45  (device_id, timestamp, state, value)67 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,89    '2021-01-01 01:11:11', 'on', 'event 1-1');1011 -- Event 1-21213 INSERT INTO latest_events_by_device1415 (device_id, timestamp, state, value)1617 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,1819    '2021-01-01 02:22:22', 'off', 'event 1-2');2021 -- Event 1-32223 INSERT INTO latest_events_by_device2425  (device_id, timestamp, state, value)2627 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,2829   '2021-01-01 03:33:33', 'on', 'event 1-3');3031 -- Event 2-13233 INSERT INTO latest_events_by_device3435 (device_id, timestamp, state, value)3637 VALUES (22222222-aaaa-bbbb-cccc-12345678abcd,3839  '2021-02-02 01:11:11', 'off', 'event 2-1');4041 -- Event 3-14243 INSERT INTO latest_events_by_device4445 (device_id, timestamp, state, value)4647 VALUES (33333333-aaaa-bbbb-cccc-12345678abcd,4849  '2021-03-03 01:11:11', 'off', 'event 3-1');1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.

查询:

CQL

1 -- Find all the latest events with state 'on'23 SELECT state, device_id, timestamp, value45 FROM   latest_events_by_state67 WHERE  state = 'on';8910 state | device_id                 | timestamp                     | value1112-------+--------------------------------------+---------------------------------+-----------1314 on | 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3151617 -- Find all the latest events with state 'off'1819 SELECT state, device_id, timestamp, value2021 FROM   latest_events_by_state2223 WHERE  state = 'off';242526 state | device_id                  | timestamp                       | value2728-------+--------------------------------------+---------------------------------+-----------2930 off | 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 | event 2-13132 off | 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 | event 3-11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.

物化视图解决方案具有以下特点:

  • 适用性:基于状态的查询返回100K行/100MB或更少的数据。
  • 优点:视图自动维护和出色的性能。
  • 缺点:物化视图有一些限制;数据分布可能会出现偏差。

为了支持多租户,可以把表的主键改成PRIMARY KEY((tenant,device_id))或者PRIMARY KEY((tenant),device_id),物化视图的主键改成PRIMARY KEY((tenant,state),device_id)。多租户也可能有助于改善数据分布。

只要了解并愿意抵消物化视图的限制,这一数据模型就可以成为许多应用程序的简单、有效和高效的选择。这种数据模型的另一个不太明显的优势是从Apache Pulsar或Apache Kafka等事件流平台提供数据是多么容易。所有事件都可以转到基表,而其余的由物化视图处理。

5、解决方案二:二级索引

第二种解决方案需要一个新表和一个二级索引。该表与物化视图解决方案中的表相同。表latest_events_by_device是一个单行分区表,其中每个分区对应一个设备,每一行对应最新的已知事件。此表的目的是仅获取物联网设备报告的最新事件的快照。为该表创建二级索引latest_events_by_state_2i,用于根据状态查询最新事件。

同样,完全相同的数据被插入到表events_by_device和latest_events_by_device中。对于后者,插入变为更新插入,将行更新为最新事件。

模式:

CQL

1-- Latest known events by device23 CREATE TABLE latest_events_by_device (45  device_id  UUID,67 timestamp  TIMESTAMP,89  state      TEXT,1011  value      TEXT,1213  PRIMARY KEY((device_id))1415 );161718 -- Latest events by state1920 CREATE INDEX latest_events_by_state_2i2122 ON latest_events_by_device (state);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.

数据:

CQL

1 -- Event 1-123 INSERT INTO latest_events_by_device45  (device_id, timestamp, state, value)67 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,89  '2021-01-01 01:11:11', 'on', 'event 1-1');1011 -- Event 1-21213 INSERT INTO latest_events_by_device1415   (device_id, timestamp, state, value)1617 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,1819  '2021-01-01 02:22:22', 'off', 'event 1-2');2021 -- Event 1-32223 INSERT INTO latest_events_by_device2425  (device_id, timestamp, state, value)2627 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,2829   '2021-01-01 03:33:33', 'on', 'event 1-3');3031 -- Event 2-13233  INSERT INTO latest_events_by_device3435  (device_id, timestamp, state, value)3637 VALUES (22222222-aaaa-bbbb-cccc-12345678abcd,3839   '2021-02-02 01:11:11', 'off', 'event 2-1');4041 -- Event 3-14243 INSERT INTO latest_events_by_device4445    (device_id, timestamp, state, value)4647  VALUES (33333333-aaaa-bbbb-cccc-12345678abcd,4849   '2021-03-03 01:11:11', 'off', 'event 3-1');1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.

查询:

CQL

1 -- Find all the latest events with state 'on'23 SELECT state, device_id, timestamp, value45 FROM   latest_events_by_device67 WHERE  state = 'on';8910  state | device_id                            | timestamp                       | value1112-------+--------------------------------------+---------------------------------+-----------1314  on | 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3151617 -- Find all the latest events with state 'off'1819 SELECT state, device_id, timestamp, value2021 FROM   latest_events_by_device2223 WHERE  state = 'off';242526  state | device_id                  | timestamp                       | value2728-------+--------------------------------------+---------------------------------+-----------2930 off | 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 | event 3-13132 off | 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 | event 2-11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.

二级索引方案具有以下特点:

  • 适用性:基于状态的查询返回100K行/100MBs或更多的数据;基于状态的查询很少执行。
  • 优点:在检索大型结果集时,可以更好地在集群中的节点之间分配查询工作负载。
  • 缺点:二级索引有一些限制;对于实时应用程序,性能可能会变得让人不满意。

在某些情况下,这个数据模型可能是一个合理的选择。特别是,当通过将表主键更改为PRIMARY KEY((tenant),device_id)来引入多租户时,可以达到使用二级索引进行实时事务查询的好时机。那是在基于分区键和查询谓词中指定的索引列从大型多行分区中检索行时。

6、解决方案三:状态分区表

第三种解决方案依赖于表latest_events_by_state使用状态来组织和查询最新事件。每次向该表中插入具有某种状态的事件时,都必须删除同一物联网设备的具有其他状态的任何过时事件。在这个示例中,每个事件都有一个插入和一个删除,因为只有两个唯一状态。如果有三种可能的状态,每个新事件将导致一次插入和两次删除。

模式:

CQL

1 -- Latest events by state23 CREATE TABLE latest_events_by_state (45  state     TEXT,67 device_id  UUID,89  timestamp  TIMESTAMP,1011 value      TEXT,1213 PRIMARY KEY((state), device_id)1415 );1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.

数据:

CQL

1-- Event 1-123 INSERT INTO latest_events_by_state45  (state, device_id, timestamp, value)67 VALUES ('on', 11111111-aaaa-bbbb-cccc-12345678abcd,89  '2021-01-01 01:11:11', 'event 1-1');1011 DELETE FROM latest_events_by_state1213 WHERE state = 'off' AND1415 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1617 -- Event 1-21819 INSERT INTO latest_events_by_state2021    (state, device_id, timestamp, value)2223 VALUES ('off', 11111111-aaaa-bbbb-cccc-12345678abcd,2425      '2021-01-01 02:22:22', 'event 1-2');2627 DELETE FROM latest_events_by_state2829 WHERE state = 'on' AND3031  device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;3233 -- Event 1-33435 INSERT INTO latest_events_by_state3637   (state, device_id, timestamp, value)3839 VALUES ('on', 11111111-aaaa-bbbb-cccc-12345678abcd,4041    '2021-01-01 03:33:33', 'event 1-3');4243 DELETE FROM latest_events_by_state4445 WHERE state = 'off' AND4647   device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;4849 -- Event 2-15051 INSERT INTO latest_events_by_state5253  (state, device_id, timestamp, value)5455 VALUES ('off', 22222222-aaaa-bbbb-cccc-12345678abcd,5657   '2021-02-02 01:11:11', 'event 2-1');5859 DELETE FROM latest_events_by_state6061 WHERE state = 'on' AND6263  device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;6465 -- Event 3-16667 INSERT INTO latest_events_by_state6869 (state, device_id, timestamp, value)7071 VALUES ('off', 33333333-aaaa-bbbb-cccc-12345678abcd,7273    '2021-03-03 01:11:11', 'event 3-1');7475 DELETE FROM latest_events_by_state7677 WHERE state = 'on' AND7879  device_id = 33333333-aaaa-bbbb-cccc-12345678abcd;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.125.126.127.128.129.130.131.132.133.134.135.136.137.138.139.140.141.142.143.144.145.146.147.148.149.150.151.152.153.154.155.156.157.

查询:

CQL

1-- Find all the latest events with state 'on'23 SELECT state, device_id, timestamp, value45 FROM   latest_events_by_state67 WHERE  state = 'on';8910 state | device_id                  | timestamp                       | value1112-------+--------------------------------------+---------------------------------+-----------1314  on | 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3151617 -- Find all the latest events with state 'off'1819 SELECT state, device_id, timestamp, value2021 FROM   latest_events_by_state2223 WHERE  state = 'off';242526 state | device_id                  | timestamp                       | value2728-------+--------------------------------------+---------------------------------+-----------2930 off | 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 | event 2-13132 off | 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 | event 3-11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.

状态分区表解决方案具有以下特点:

  • 适用性:基于状态的查询返回100K行/100MB或更少的数据。
  • 优点:出色的性能。
  • 缺点:需要额外的删除来维护表;可能需要采取措施防止Cassandra的问题;数据分布可能会出现偏差。

在大多数情况下,这三个缺点都不应被视为严重障碍。额外的删除相当于额外的写入,Cassandra可以轻松扩展以处理更多写入。鉴于插入和删除一次又一次地应用于相同的行,Cassandra很可能在MemTable中而不是在SSTable中得到解决,这可以显著地减少Cassandra的总数。例如,对于一个给定的物联网设备,即使是频繁的状态更新都命中同一个MemTable也只能导致一个Cassandra。依旧建议监控表指标以排除任何潜在问题。最后但同样重要的是,数据分布取决于数据和应用程序特征。在本文的最后一个解决方案中,完全控制了数据分布。

可以通过将表主键更改为PRIMARYKEY((tenant,state),device_id)轻松支持多个租户。多租户也可能有助于改善数据分布。总体而言,在性能方面,该解决方案应该可以与物化视图解决方案相媲美。

7、解决方案四:多个表

第四种解决方案的特点是每个状态都有一个单独的表格。对表latest_on_events_by_device的每次插入都必须伴随着从表latest_off_events_by_device中删除,反之亦然。这是为了确保最新事件始终取消同一设备的任何具有不同状态的过时事件。对表的基于状态的查询可能会变得成本非常高昂,因为它们必须扫描表中的所有分区。

模式:

CQL

1 -- Latest 'on' events by device23 CREATE TABLE latest_on_events_by_device (45  device_id  UUID,67 timestamp  TIMESTAMP,89 value      TEXT,1011 PRIMARY KEY((device_id))1213 );141516-- Latest 'off' events by device1718 CREATE TABLE latest_off_events_by_device (1920 device_id  UUID,2122  timestamp  TIMESTAMP,2324 value      TEXT,2526 PRIMARY KEY((device_id))2728 );1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.

数据:

CQL

1 - Event 1-123 INSERT INTO latest_on_events_by_device45  (device_id, timestamp, value)67VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,89  '2021-01-01 01:11:11', 'event 1-1');1011 DELETE FROM latest_off_events_by_device1213 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1415-- Event 1-21617 INSERT INTO latest_off_events_by_device1819 (device_id, timestamp, value)2021 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,2223  '2021-01-01 02:22:22', 'event 1-2');2425 DELETE FROM latest_on_events_by_device2627 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;2829-- Event 1-33031 INSERT INTO latest_on_events_by_device3233 (device_id, timestamp, value)3435 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,3637   '2021-01-01 03:33:33', 'event 1-3');3839 DELETE FROM latest_off_events_by_device4041 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;4243-- Event 2-14445 INSERT INTO latest_off_events_by_device4647    (device_id, timestamp, value)4849 VALUES (22222222-aaaa-bbbb-cccc-12345678abcd,5051    '2021-02-02 01:11:11', 'event 2-1');5253 DELETE FROM latest_on_events_by_device5455 WHERE device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;5657-- Event 3-15859 INSERT INTO latest_off_events_by_device6061  (device_id, timestamp, value)6263 VALUES (33333333-aaaa-bbbb-cccc-12345678abcd,6465  '2021-03-03 01:11:11', 'event 3-1');6667 DELETE FROM latest_on_events_by_device6869 WHERE device_id = 33333333-aaaa-bbbb-cccc-12345678abcd;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.125.126.127.128.129.130.131.132.133.134.135.136.137.138.139.140.141.

查询:

CQL

1 -- Find all the latest events with state 'on'23 SELECT device_id, timestamp, value45FROM   latest_on_events_by_device;678 device_id               | timestamp                       | value910--------------------------------------+---------------------------------+-----------1112 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3131415-- Find all the latest events with state 'off'1617 SELECT device_id, timestamp, value1819 FROM   latest_off_events_by_device;202122 device_id             | timestamp                       | value2324--------------------------------------+---------------------------------+-----------2526 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 | event 3-12728 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 | event 2-11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.

多表解决方案具有以下特点:

  • 适用性:基于状态的查询返回100K行/100MBs或更多的数据;基于状态的查询很少执行。
  • 优点:在检索大型结果集时,可以更好地在集群中的节点之间分配查询工作负载。
  • 缺点:实时应用程序的性能可能无法令人满意;需要额外的删除来维护表;可能需要采取措施防止与Cassandra有关的问题。

该方案在查询性能上与二级索引方案相当。可以通过将表主键更改为PRIMARYKEY((tenant,device_id))或PRIMARYKEY((tenant),device_id)来支持多个租户。虽然在实践中不推荐这种解决方案,但这种数据模型真正有趣的是它如何为接下来讨论的可定制分区做好准备。

8、解决方案五:可自定义的分区

最终解决方案基于为每个状态使用单独的表的想法。但是这一次,使用人工桶对表进行分区。桶值很容易使用来自设备UUID标识符的用户定义函数散列来计算。在这一示例中,该函数从UUID文字中提取前三位,将生成的十六进制数转换为十进制数,并返回十进制数除以3的余数。因此,最多可以有三个桶或每个表的分区,值为0、1或2。在这一示例中,所有的设备标识符都映射到存储桶0只是巧合。由于版本4UUID是随机生成的,因此对于大量事件,数据应该或多或少均匀分布在三个存储桶中。

与之前的数据模型类似,每次对表latest_on_events_by_bucket的插入都必须伴随着从表latest_off_events_by_bucket中删除,反之亦然。基于状态的查询的性能取决于分区,并且分区是可定制的。

模式:

CQL

1-- Custom hash function23 CREATE FUNCTION hash(id UUID)45 RETURNS NULL ON NULL INPUT67 RETURNS INT89 LANGUAGE Java AS1011 'return Integer.parseInt(id.toString().substring(0,3),16) % 3;';121314-- Latest 'on' events by device1516 CREATE TABLE latest_on_events_by_bucket (1718 bucket     INT,1920 device_id  UUID,2122 timestamp  TIMESTAMP,2324 value      TEXT,2526 PRIMARY KEY((bucket), device_id)2728 );293031-- Latest 'off' events by device3233 CREATE TABLE latest_off_events_by_bucket (3435 bucket     INT,3637 device_id  UUID,3839 timestamp  TIMESTAMP,4041 value      TEXT,4243 PRIMARY KEY((bucket), device_id)4445 );1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.

数据:

CQL

1-- Event 1-123 INSERT INTO latest_on_events_by_bucket45  (bucket, device_id, timestamp, value)67 VALUES (hash(11111111-aaaa-bbbb-cccc-12345678abcd),89 11111111-aaaa-bbbb-cccc-12345678abcd,1011   '2021-01-01 01:11:11', 'event 1-1');1213 DELETE FROM latest_off_events_by_bucket1415 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND1617 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1819-- Event 1-22021 INSERT INTO latest_off_events_by_bucket2223 (bucket, device_id, timestamp, value)2425 VALUES (hash(11111111-aaaa-bbbb-cccc-12345678abcd),2627  11111111-aaaa-bbbb-cccc-12345678abcd,2829 '2021-01-01 02:22:22', 'event 1-2');3031 DELETE FROM latest_on_events_by_bucket3233 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND3435 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;3637-- Event 1-33839 INSERT INTO latest_on_events_by_bucket4041 (bucket, device_id, timestamp, value)4243 VALUES (hash(11111111-aaaa-bbbb-cccc-12345678abcd),4445    11111111-aaaa-bbbb-cccc-12345678abcd,4647     '2021-01-01 03:33:33', 'event 1-3');4849 DELETE FROM latest_off_events_by_bucket5051 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND5253 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;5455 -- Event 2-15657 INSERT INTO latest_off_events_by_bucket5859  (bucket, device_id, timestamp, value)6061 VALUES (hash(22222222-aaaa-bbbb-cccc-12345678abcd),6263  22222222-aaaa-bbbb-cccc-12345678abcd,6465   '2021-02-02 01:11:11', 'event 2-1');6667 DELETE FROM latest_on_events_by_bucket6869 WHERE bucket = hash(22222222-aaaa-bbbb-cccc-12345678abcd) AND7071 device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;7273-- Event 3-17475 INSERT INTO latest_off_events_by_bucket7677  (bucket, device_id, timestamp, value)7879 VALUES (hash(33333333-aaaa-bbbb-cccc-12345678abcd),8081  33333333-aaaa-bbbb-cccc-12345678abcd,8283 '2021-03-03 01:11:11', 'event 3-1');8485 DELETE FROM latest_on_events_by_bucket8687 WHERE bucket = hash(33333333-aaaa-bbbb-cccc-12345678abcd) AND8889 device_id = 33333333-aaaa-bbbb-cccc-12345678abcd;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.125.126.127.128.129.130.131.132.133.134.135.136.137.138.139.140.141.142.143.144.145.146.147.148.149.150.151.152.153.154.155.156.157.158.159.160.161.162.163.164.165.166.167.168.169.170.171.172.173.174.175.176.177.

查询:

CQL

1 -- Find all the latest events with state 'on'23 SELECT bucket, device_id, timestamp, value45 FROM   latest_on_events_by_bucket67 WHERE  bucket IN (0,1,2);8910 bucket | device_id               | timestamp                  | value1112--------+--------------------------------------+---------------------------------+-----------1314 0 | 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3151617-- Find all the latest events with state 'off'1819 SELECT bucket, device_id, timestamp, value2021 FROM   latest_off_events_by_bucket2223 WHERE  bucket IN (0,1,2);242526 bucket | device_id               | timestamp                     | value2728--------+--------------------------------------+---------------------------------+-----------2930  0 | 22222222-aaaa-bbbb-cccc-12345678abcd | 2021-02-02 01:11:11.000000+0000 | event 2-13132 0 | 33333333-aaaa-bbbb-cccc-12345678abcd | 2021-03-03 01:11:11.000000+0000 | event 3-11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.

可定制的分区方案具有以下特点:

  • 适用性:定制时可满足不同要求。
  • 优点:灵活性;可以通过自定义分区来优化性能。
  • 缺点:必须提供良好的分区功能;需要额外的删除来维护表;可能需要采取措施防止与Cassandra有关的问题。

选择一个良好的分区函数是一个很好的问题。虽然这可能会增加一点复杂性,但该解决方案可以完全控制数据分区和查询性能。找到一个良好的分区函数将取决于特定的数据和应用程序要求,并且可能需要一些经验和实验。例如,从1个分区检索100行通常比从10个分区检索100行快,但从1个分区检索100万行通常比从10个分区检索100万行慢。接下来,额外的删除相当于额外的写入,Cassandra可以轻松扩展以处理更多写入。

鉴于插入和删除一次又一次地应用于相同的行,Cassandra很可能在MemTable中而不是在SS Table中得到解决,这可以显著地减少Cassandra的总数。例如,对于一个给定的物联网设备,即使是频繁的状态更新都命中同一个Mem Table也只能导致一个Cassandra。依旧建议监控表指标以排除任何潜在问题。最后但同样重要的是,数据分布取决于数据和应用程序特征。在本文的最后一个解决方案中,完全控制了数据分布。

这种数据模型提供了极大的灵活性。通过将每个表的主键更改为PRIMARY KEY((tenant,bucket),device_id)可以实现多租户。更重要的是,可以更改分区函数以增加或减少分区的数量。检索较小结果集的查询应访问较少数量的分区以获得更好的性能。检索更大结果集的查询应访问更多分区以更好地分配工作负载。可以针对不同的状态和租户使用不同的功能以实现最佳性能。更好的分区应该会带来更好的性能。

9、结论

本文定义了基于状态管理最新物联网事件的问题,确定了它的挑战,并描述了如何在Apache Cassandra中使用五种不同的数据模型来解决它。此外还阐述了每个数据模型的适用性、优缺点。最终的建议是关注物化视图、状态分区表和可自定义的分区数据模型。选择前两个是因为它们简单易用。当采用其他选项时,考虑可定制的分区以获得最大的灵活性。最后,开放探索新的可能解决方案,这些解决方案可能会将一些计算推向应用程序或依赖专门的搜索索引和其他技术。

原文链接:https://dzone.com/articles/five-data-models-for-iot-managing-the-latest-iot-e

来源: 51CTO技术栈

相关内容