我正在处理涉及Azure IOT Hub和Azure Functions的Azure项目。
我有大约50个传感器每隔10秒向IOT Hub发送一条新消息。
每次Azure IOT Hub收到新消息时,我都希望执行一个函数来读取已发送的消息并将其保存到Azure Table存储中。
目前,关于我应该使用什么样的Azure Table存储设计,我有点迷失。到目前为止,这是我提出的表存储设计:
[PartitionKey][RowKey][TimeStamp][SensorSerial][Reading][Type]
这是模拟Azure Storage Explorer中数据的样子:
[GroupA][?][2017-05-03T12:20:22.713Z][xxx][60][Temperature]
[GroupA][?][2017-05-03T12:25:22.713Z][xxx][61][Temperature]
[GroupA][?][2017-05-03T12:30:22.713Z][xxx][59][Temperature]
[GroupB][?][2017-05-03T12:35:22.713Z][yyy][90][Humidity]
[GroupB][?][2017-05-03T12:40:22.713Z][yyy][92][Humidity]
我已经把RowKey留在"?"目前因为它与手头的问题有关。
问题,我希望能够根据SensorSerial和指定的时间范围查询表存储数据 - ,例如。获取最近15秒的所有xxx读数。
以下查询始终不返回任何数据:
TableQuery<Readings> rangeQuery = new TableQuery<Readings>().Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("SensorSerial", QueryComparisons.Equal, "xxx"),
TableOperators.And,
TableQuery.GenerateFilterConditionForDate("TimeStamp",
QueryComparisons.GreaterThanOrEqual, DateTime.Now.AddSeconds(-15))));
从我目前所读到的内容来看,我不确定为什么会这样 - 无法根据TimeStamp字段过滤数据。因此,您必须将RowKey用作某种伪TimeStamp日期时间刻度字段。
因此,为了解决这个问题,我计划将其用作我的RowKey vaue
var RowKey = string.Format("{0:D19}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
哪个会满足此查询并返回必要的值:
TableQuery<Readings> query = new TableQuery<SensorEntity>().Where(
TableQuery.CombineFilters(
(TableQuery.GenerateFilterCondition("SensorSerial", QueryComparisons.Equal, "xxxx")),
TableOperators.And,
(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThanOrEqual,
"2519084875883616261"))));
但是,我可能在这里错了,此方法可能会导致某些问题,因为以下 :
如果两个或多个传感器以同一时间/间隔传输数据怎么办? RowKey必须是唯一的,一个传感器插入一个新行的时刻进入Azure存储,另一个将无法继续。
我可以运行代码,希望传输/数据处理/插入导致足够的延迟,永远不会导致任何问题,但依赖它会很糟糕。
有更好的方法吗?一种更安全的故障安全方法,允许我根据指定的时间和唯一的设备标识符查询Azure数据表存储?
答案 0 :(得分:4)
让我们先谈谈你目前的做法。
你现在采取的方法是相当不错的。您的方法的优点是您正在使用reverse ticks (DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks)
,这将确保将最新数据添加到表的顶部而不是表的底部,这样只要您查询最后一个x数小时/小时的数据,检索速度非常快。
在路上我看到了这种方法的一些问题:
Partition Scans
正在发生的情况。这比Full Table Scans
略好,但如果可能的话应该避免。scalability limits
,因为所有读/写都只发生在一个表上。这将对性能产生不利影响。可能的解决方案
一种可能的解决方案(现在考虑您的查询是针对传感器的)是为每个传感器设置单独的表,然后将该传感器的数据存储在指定的表中。我用这种方法看到的优点是:
PartitionKey
作为反向刻度,将RowKey
用于您喜欢的任何其他值。我建议为PartitionKey
存储具有更高粒度(例如一小时)的刻度,并保持RowKey
相同。这将确保您最终不会创建大量分区。SensorA
表可以在Storage Account A
中,而Sensor B
表可以在Storage Account B
中。这样,您实际上就可以平衡不同表/存储帐户之间的流量,从而实现更好的可扩展性和吞吐量。显然,这种方法的缺点是它会给您带来更多管理上的麻烦。您需要拥有某种主数据库,您可以在其中保持传感器与其关联存储帐户之间的关联。这种方法的另一个缺点是你将无法只查询时间戳(我的第二个问题)。为此,您可以使用您正在采用的方法在另一个存储帐户中保留一个表。
对于您的评论What if two or more sensors being to transmit data at the same time/interval? RowKey must be unique, the moment one sensor inserts a new row into Azure Storage, the other will no linger be able to.
,基本上RowKey在Partition
中必须是唯一的,换句话说PartitionKey + RowKey
组合在表格中必须是唯一的。所以我不认为这会成为一个问题。
答案 1 :(得分:0)
我个人认为每个传感器的桌子都不是一个好主意。但是要回答最后一个问题,您可以使用一个天蓝色表将传感器序列映射到存储帐户和/或表名,例如:
PartitionKey,RowKey,StorageAccount,TableName
“SensorSetting.SensorStorage”,DeviceId,”AccountName”,”TableName”
我使用与上面类似的“ GlobalSettings”类型存储,以便按设备等存储设置,例如
PartitionKey,RowKey,StorageAccount,TableName
“GlobalSetting”,”{SettingName}”,”{SettingValue}”
“SensorSetting.SensorStorage”,”{SerialNo}”,”{AccountName}”,”{TableName}”
“SensorSetting.TemperatureThreshold”,”{SerialNo}”,”{SomeValue}”
然后使用PartitionKey / RowKey作为“ GlobalSetting”和SettingName易于阅读的全局设置
类似地,使用PartitionKey / RowKey作为“ SensorSetting。{SettingName}”和SerialNo
可以轻松读取传感器的设置