Azure数据表 - 正确使用RowKey作为DateTime.Ticks?

时间:2017-05-03 13:55:26

标签: c# azure azure-storage

我正在处理涉及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数据表存储?

2 个答案:

答案 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

可以轻松读取传感器的设置