译者 | 李睿
审校 | 孙淑娟

Apache Cassandra是详解型大规模管理物联网和时间序列数据的一个可靠选择。在Cassandra中存储、物联网查询和分析物联网设备生成的种数时间序列的最流行用例已经得到很好的理解和记录。在通常情况下,据模时间序列是详解型根据其源物联网设备存储和查询的。但是物联网,还有另一类物联网应用程序需要快速访问由一组物联网设备基于已知状态生成的种数最新数据。此类应用程序需要回答的据模问题是:哪些物联网设备或传感器当前正在报告特定状态?本文将重点关注这个问题,并提供五种可能的详解型数据建模解决方案,以便在Cassandra中有效地回答这个问题。物联网
物联网正在生成大量需要存储、据模查询和分析的详解型时间序列数据。Apache Cassandra是物联网这项任务的好选择:不仅因为它的速度、可靠性和可扩展性,种数还因为它的内部数据模型内置了对时间排序数据的支持。亿华云
在Cassandra中,时间序列通常由源(例如物联网设备或传感器)或主题(例如参数或指标)存储和检索。有许多很好的资源非常详细地介绍了这个主题,包括这个会议演示视频,以及用于传感器数据和时间序列的即用型Cassandra数据模型。
本文研究了一些相关的物联网用例,它们需要管理来自许多物联网设备的最新数据的快照。此外,需要根据物联网设备报告的特定状态来查询或过滤这样的快照。换句话说,应该能够在Cassandra中快速回答这个问题:哪些物联网设备当前正在报告特定状态?对于许多现实生活中的用例,这个问题听起来更像是:
智能家居中哪些灯是打开(关闭)的?停车场中当前有哪些停车位被占用(空置)?当前在特定位置附近有哪些车辆可用(不可用)?当前在某个区域触发(激活或禁用)哪些安全警报?建筑物中当前打开(关闭、锁定、解锁)哪些门?哪些火灾探测传感器当前报告传感器网络中的异常(正常待机、错误)状态?本文章更加正式地定义了这些问题,并通过示例CQL实现提出了五个实用的解决方案。
给定一组物联网设备或传感器,它们生成包含时间戳、b2b信息网数据点和状态的按时间顺序排列的事件序列,查找所有物联网设备报告的具有已知状态的最新事件。这个问题的三个关键组成部分如下所示:
输入由物联网设备生成的时间序列组成。时间序列通常存储在一个或多个Cassandra表中。中间视图仅是物联网设备报告的最新事件的快照。可以单独显式存储最新事件,也可以根据输入动态计算它们。最终结果是所有具有已知状态的最新事件。具有相同状态的最新事件应该存储在一起或易于计算。以下确定了基于状态管理最新物联网事件的几个挑战:
最新事件的快照不断发展。可能需要额外的工作来增量捕获任何更改。事件发生的频率通常是不可预测的。仅基于事件的亿华云计算时间戳组件可能难以对事件进行分区和组织。一个状态通常只能采用几个唯一值。基于低基数列对数据进行分区和索引可能会导致大分区。使用以下运行示例作为起点。表events_by_device是输入。这张具有多行分区的表旨在存储时间序列,这样每个分区对应一个设备,分区中的行表示具有时间戳、状态和值的事件。每个分区中的事件始终按其时间戳降序排序。该表实际上为每个分区存储一个时间序列。将五个事件插入表中并检索一个设备的时间序列。此外,在第二个查询中,演示可以动态计算所有设备的所有最新事件。需要注意的是不应该依赖这个查询来解决问题:它可能会变得代价高昂,因为它访问表中的每个分区。
模式:CQL
复制1 -- All events by device23 CREATE TABLE events_by_device (45 device_id UUID,67 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 INTOevents_by_device
45 (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 INTOevents_by_device
1415 (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 INTOevents_by_device
2425 (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 INTOevents_by_device
3435 (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 INTOevents_by_device
4445 (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,value
45 FROMevents_by_device
67 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;8910device_id | timestamp | state |value
1112--------------------------------------+---------------------------------+-------+-----------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,value
2425 FROMevents_by_device
2627 PER PARTITION LIMIT 1;282930 device_id | timestamp | state |value
3132--------------------------------------+---------------------------------+-------+-----------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,value
45 FROMevents_by_device
67 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;8910device_id | timestamp | state |value
1112--------------------------------------+---------------------------------+-------+-----------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,value
2425 FROMevents_by_device
2627 PER PARTITION LIMIT 1;282930 device_id | timestamp | state |value
3132--------------------------------------+---------------------------------+-------+-----------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示例,在此准备描述具有不同特征的五种解决方案。
第一个解决方案需要一个新表和一个物化视图。表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 * FROMlatest_events_by_device
2324 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 INTOlatest_events_by_device
45 (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 INTOlatest_events_by_device
1415 (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 INTOlatest_events_by_device
2425 (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 INTOlatest_events_by_device
3435 (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 INTOlatest_events_by_device
4445 (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 on23 SELECT state, device_id, timestamp,value
45 FROMlatest_events_by_state
67 WHERE state = on;8910 state | device_id | timestamp |value
1112-------+--------------------------------------+---------------------------------+-----------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 off1819 SELECT state, device_id, timestamp,value
2021 FROMlatest_events_by_state
2223 WHERE state = off;242526 state | device_id | timestamp |value
2728-------+--------------------------------------+---------------------------------+-----------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等事件流平台提供数据是多么容易。所有事件都可以转到基表,而其余的由物化视图处理。
第二种解决方案需要一个新表和一个二级索引。该表与物化视图解决方案中的表相同。表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 CREATEINDEX latest_events_by_state_2i
2122 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 INTOlatest_events_by_device
45 (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 INTOlatest_events_by_device
1415 (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 INTOlatest_events_by_device
2425 (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 INTOlatest_events_by_device
3435 (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 INTOlatest_events_by_device
4445 (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 on23 SELECT state, device_id, timestamp,value
45 FROMlatest_events_by_device
67 WHERE state = on;8910 state | device_id | timestamp |value
1112-------+--------------------------------------+---------------------------------+-----------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 off1819 SELECT state, device_id, timestamp,value
2021 FROMlatest_events_by_device
2223 WHERE state = off;242526 state | device_id | timestamp |value
2728-------+--------------------------------------+---------------------------------+-----------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)来引入多租户时,可以达到使用二级索引进行实时事务查询的好时机。那是在基于分区键和查询谓词中指定的索引列从大型多行分区中检索行时。
第三种解决方案依赖于表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 INTOlatest_events_by_state
45 (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 FROMlatest_events_by_state
1213 WHERE state = off AND1415 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1617 -- Event 1-21819 INSERT INTOlatest_events_by_state
2021 (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 FROMlatest_events_by_state
2829 WHERE state = on AND3031 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;3233 -- Event 1-33435 INSERT INTOlatest_events_by_state
3637 (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 FROMlatest_events_by_state
4445 WHERE state = off AND4647 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;4849 -- Event 2-15051 INSERT INTOlatest_events_by_state
5253 (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 FROMlatest_events_by_state
6061 WHERE state = on AND6263 device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;6465 -- Event 3-16667 INSERT INTOlatest_events_by_state
6869 (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 FROMlatest_events_by_state
7677 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 on23 SELECT state, device_id, timestamp,value
45 FROMlatest_events_by_state
67 WHERE state = on;8910 state | device_id | timestamp |value
1112-------+--------------------------------------+---------------------------------+-----------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 off1819 SELECT state, device_id, timestamp,value
2021 FROMlatest_events_by_state
2223 WHERE state = off;242526 state | device_id | timestamp |value
2728-------+--------------------------------------+---------------------------------+-----------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)轻松支持多个租户。多租户也可能有助于改善数据分布。总体而言,在性能方面,该解决方案应该可以与物化视图解决方案相媲美。
第四种解决方案的特点是每个状态都有一个单独的表格。对表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 INTOlatest_on_events_by_device
45 (device_id, timestamp, value)67VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,89 2021-01-01 01:11:11, event 1-1);1011 DELETE FROMlatest_off_events_by_device
1213 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1415-- Event 1-21617 INSERT INTOlatest_off_events_by_device
1819 (device_id, timestamp, value)2021 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,2223 2021-01-01 02:22:22, event 1-2);2425 DELETE FROMlatest_on_events_by_device
2627 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;2829-- Event 1-33031 INSERT INTOlatest_on_events_by_device
3233 (device_id, timestamp, value)3435 VALUES (11111111-aaaa-bbbb-cccc-12345678abcd,3637 2021-01-01 03:33:33, event 1-3);3839 DELETE FROMlatest_off_events_by_device
4041 WHERE device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;4243-- Event 2-14445 INSERT INTOlatest_off_events_by_device
4647 (device_id, timestamp, value)4849 VALUES (22222222-aaaa-bbbb-cccc-12345678abcd,5051 2021-02-02 01:11:11, event 2-1);5253 DELETE FROMlatest_on_events_by_device
5455 WHERE device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;5657-- Event 3-15859 INSERT INTOlatest_off_events_by_device
6061 (device_id, timestamp, value)6263 VALUES (33333333-aaaa-bbbb-cccc-12345678abcd,6465 2021-03-03 01:11:11, event 3-1);6667 DELETE FROMlatest_on_events_by_device
6869 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 on23 SELECT device_id, timestamp,value
45FROM latest_on_events_by_device;678 device_id | timestamp |value
910--------------------------------------+---------------------------------+-----------1112 11111111-aaaa-bbbb-cccc-12345678abcd | 2021-01-01 03:33:33.000000+0000 | event 1-3131415-- Find all the latest events with state off1617 SELECT device_id, timestamp,value
1819 FROM latest_off_events_by_device;202122 device_id | timestamp |value
2324--------------------------------------+---------------------------------+-----------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)来支持多个租户。虽然在实践中不推荐这种解决方案,但这种数据模型真正有趣的是它如何为接下来讨论的可定制分区做好准备。
最终解决方案基于为每个状态使用单独的表的想法。但是这一次,使用人工桶对表进行分区。桶值很容易使用来自设备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 NULLINPUT
67 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 INTOlatest_on_events_by_bucket
45 (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 FROMlatest_off_events_by_bucket
1415 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND1617 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;1819-- Event 1-22021 INSERT INTOlatest_off_events_by_bucket
2223 (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 FROMlatest_on_events_by_bucket
3233 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND3435 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;3637-- Event 1-33839 INSERT INTOlatest_on_events_by_bucket
4041 (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 FROMlatest_off_events_by_bucket
5051 WHERE bucket = hash(11111111-aaaa-bbbb-cccc-12345678abcd) AND5253 device_id = 11111111-aaaa-bbbb-cccc-12345678abcd;5455 -- Event 2-15657 INSERT INTOlatest_off_events_by_bucket
5859 (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 FROMlatest_on_events_by_bucket
6869 WHERE bucket = hash(22222222-aaaa-bbbb-cccc-12345678abcd) AND7071 device_id = 22222222-aaaa-bbbb-cccc-12345678abcd;7273-- Event 3-17475 INSERT INTOlatest_off_events_by_bucket
7677 (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 FROMlatest_on_events_by_bucket
8687 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 on23 SELECT bucket, device_id, timestamp,value
45 FROMlatest_on_events_by_bucket
67 WHERE bucket IN (0,1,2);8910 bucket | device_id | timestamp |value
1112--------+--------------------------------------+---------------------------------+-----------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 off1819 SELECT bucket, device_id, timestamp,value
2021 FROMlatest_off_events_by_bucket
2223 WHERE bucket IN (0,1,2);242526 bucket | device_id | timestamp |value
2728--------+--------------------------------------+---------------------------------+-----------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)可以实现多租户。更重要的是,可以更改分区函数以增加或减少分区的数量。检索较小结果集的查询应访问较少数量的分区以获得更好的性能。检索更大结果集的查询应访问更多分区以更好地分配工作负载。可以针对不同的状态和租户使用不同的功能以实现最佳性能。更好的分区应该会带来更好的性能。
本文定义了基于状态管理最新物联网事件的问题,确定了它的挑战,并描述了如何在Apache Cassandra中使用五种不同的数据模型来解决它。此外还阐述了每个数据模型的适用性、优缺点。最终的建议是关注物化视图、状态分区表和可自定义的分区数据模型。选择前两个是因为它们简单易用。当采用其他选项时,考虑可定制的分区以获得最大的灵活性。最后,开放探索新的可能解决方案,这些解决方案可能会将一些计算推向应用程序或依赖专门的搜索索引和其他技术。
原文链接:https://dzone.com/articles/five-data-models-for-iot-managing-the-latest-iot-e
相关文章: