Azure table 存储查询分区键

Azure table storage querying partitionkey

我正在使用 Azure table 存储通过时间戳过滤器检索数据。我看到执行速度很慢,因为时间戳不是分区键或行键。我研究了 Whosebug,发现时间戳应该转换为滴答并存储在分区键中。我做了同样的事情,在插入数据时,我采用了以下字符串并将刻度字符串插入分区键。

string currentDateTimeTick = ConvertDateTimeToTicks(DateTime.Now.ToUniversalTime()).ToString();

public static long ConvertDateTimeToTicks(DateTime dtInput)
{
    long ticks = 0;
    ticks = dtInput.Ticks;
    return ticks;
}

到这里为止都很好。但是当我试图检索最近 5 天的数据时,我无法查询分区键的刻度。我正在尝试获取最近 5 天的数据。我在下面的代码中犯了什么错误?

int days = 5;
TableQuery<MyEntity> query = new TableQuery<MyEntity>()
.Where(TableQuery.GenerateFilterConditionForDate("PartitionKey", QueryComparisons.GreaterThanOrEqual, "0"+DateTimeOffset.Now.AddDays(days).Date.Ticks));

您确定要使用刻度作为分区键吗?这意味着每个可测量的 100 ns 瞬间都成为它自己的分区。对于基于时间的数据,您可以使用分区键指定一个间隔,例如每小时、每分钟甚至每秒,然后使用具有实际时间戳的行键。

抛开这个问题,让我向您展示如何进行查询。首先让我评论一下您如何生成分区键。我建议你这样做:

var partitionKey = DateTime.UtcNow.Ticks.ToString("D18");

不要使用 DateTime.Now.ToUniversalTime() 获取当前 UTC 时间。它将在内部使用 DateTime.UtcNow,然后将其转换为本地时区,然后 ToUniversalTime() 将转换回 UTC,这很浪费(并且比您想象的更耗时)。

而您的 ConvertDateTimeToTicks() 方法除了获取 Ticks 属性 没有其他用途,所以它只是让您的代码变得更复杂而没有增加任何价值。

执行查询的方法如下:

var days = 5;
var partitionKey = DateTime.UtcNow.AddDays(-days).Ticks.ToString("D18")
var query = new TableQuery<MyEntity>().Where(
  TableQuery.GenerateFilterCondition(
    "PartitionKey",
    QueryComparisons.GreaterThanOrEqual,
    partitionKey
  )
);

分区键的格式为 18 个字符的字符串,允许您使用直接比较。

我建议您将生成分区键(和行键)的代码移到函数中,以确保在整个代码中以相同的方式生成键。

之所以使用 18 个字符,是因为 DateTime 今天以及未来数千年的 Ticks 值都使用 18 位十进制数字。如果您决定将分区键基于小时、分钟或秒而不是 100 ns ticks,那么您可以相应地缩短分区键的长度。

正如 Martin 所建议的,使用时间戳作为分区键几乎肯定不是您想要的。

分区是 Azure Table 存储中的规模单位,或多或少代表数据的物理分段。它们是一种可扩展性优化,允许您 "throw hardware" 解决存储越来越多数据的问题,同时保持 acceptable 响应时间(这在传统上是数据存储中的难点)。您可以通过为每一行分配分区键来定义数据中的分区。每行都在自己的分区中几乎是不可取的。

在 ATS 中,行键成为您在给定分区中的唯一键。所以partition key + row key的组合才是真正跨整个ATS的唯一键table.

有很多关于选择有效的分区键和行键的建议...none 其中有很多是通用的。这取决于您的数据的性质、您预期的查询模式等。

选择一个分区键,将您的数据聚合到分布合理的 "buckets" 集中。在所有条件都相同的情况下,如果您预计 table 中有 100 万行,那么通常有 10 个桶,每个桶有 100,000 行……或者 100 个桶,每个桶有 10,000 行。在查询时,您需要选择要查询的分区,因此存储桶的数量可能对您很重要。 "Buckets" 通常对应于您所在领域中的一个自然细分概念...一个桶代表美国的每个州,或者一个桶代表您公司的每个部门,等等。请注意,它没有必要(或通常可能)拥有完美分布的水桶...以合理的努力尽可能靠近。

您可能故意不均匀分布的一个示例是,如果您打算按存储桶改变查询模式...存储桶 A 将收到大量便宜、快速的查询,而存储桶 B 将收到更少、更昂贵的查询,等等。或者,也许 A 桶数据将保持不变,而 B 桶数据经常变化。这也可以通过多个 table 来完成...所以没有 "one size fits all" 答案。

鉴于我们对您的问题了解有限,我喜欢 Martin 关于使用时间跨度作为分区键的建议。小跨度将导致许多分区,并且(除其他外)使利用多个时间跨度的查询相对昂贵。更大的跨度会导致跨跨度的聚合成本更低,但会导致更大的分区,从而导致分区内的查询更昂贵(它还会使识别 suitable 行键可能更具挑战性)。

最终您可能需要尝试一些选项来找到最适合table您的数据和预期查询的选项。

另一条建议...不要害怕考虑在多个数据存储中复制数据以适应广泛不同的查询类型。并非每个查询都可以针对单个模式或存储配置有效地工作。跨商店同步数据所需的工作量可能少于您随心所欲的弯曲查询技术所需的工作量。

more on Partition and Row key choices

also here

祝你好运!

上面的答案中没有提到的一件事是,Azure 将检测您是否为分区键使用顺序的、始终增加或始终减少的值并创建 "range partitions"。范围分区对具有顺序唯一 PartitionKey 值的实体进行分组,以提高范围查询的性能。如果没有范围分区,如上所述,范围查询将需要跨越分区边界或服务器边界,这会降低查询性能。范围分区发生在幕后,由 Azure 决定,而不是你。

现在,如果您想进行批量插入,比方说每分钟一次,您仍然需要将时间戳分区键展平,例如四舍五入到最近的分钟。您只能使用相同的分区键进行批量插入。