改进 Azure Table 存储查询性能的建议

Suggestions to improve Azure Table Storage query performance

我们在 Azure Table 中有一个 table 存储,自从它新实施以来,目前有 50,000 个项目。

PartitionKey: 字符串形式的日期时间值
RowKey: 字符串形式的数值

我们使用TableQuery生成过滤条件。 PartitionKey 过滤器类似于:PartitionKey ge '201801240000000000' && "PartitionKey lt '201806220000000000'"

遗憾的是,我们不能使用 RowKey 过滤器,因为我们需要两个日期之间的数据。

要获取大约一个月的数据,大约需要 5 秒。而要获取大约3个月的时间,则需要更长的时间。

虽然我们有缓存策略,但第一次获取数据需要很长时间。就像数据过滤器在日期发生变化时需要很长时间一样。

如有任何改进性能的建议,我们将不胜感激。

据我从您的 post 中可以看出,您遇到的最大问题是您的查询在一个查询中跨越多个分区。这不是性能的最佳选择。根据下面的列表,您处于 Partition ScanTable Scan 之间,因为您 指定分区键,但您正在使用多个分区键。

  • A Point Query is the most efficient lookup to use and is recommended to be used for high-volume lookups or lookups requiring lowest latency. Such a query can use the indexes to locate an individual entity very efficiently by specifying both the PartitionKey and RowKey values. For example: $filter=(PartitionKey eq 'Sales') and (RowKey eq '2')
  • Second best is a Range Query that uses the PartitionKey and filters on a range of RowKey values to return more than one entity. The PartitionKey value identifies a specific partition, and the RowKey values identify a subset of the entities in that partition. For example: $filter=PartitionKey eq 'Sales' and RowKey ge 'S' and RowKey lt 'T'
  • Third best is a Partition Scan that uses the PartitionKey and filters on another non-key property and that may return more than one entity. The PartitionKey value identifies a specific partition, and the property values select for a subset of the entities in that partition. For example: $filter=PartitionKey eq 'Sales' and LastName eq 'Smith'
  • A Table Scan does not include the PartitionKey and is very inefficient because it searches all of the partitions that make up your table in turn for any matching entities. It will perform a table scan regardless of whether or not your filter uses the RowKey. For example: $filter=LastName eq 'Jones'
  • Queries that return multiple entities return them sorted in PartitionKey and RowKey order. To avoid resorting the entities in the client, choose a RowKey that defines the most common sort order.

来源:Azure Storage Table Design Guide: Designing Scalable and Performant Tables

另一篇非常有用的文章是这篇文章:What PartitionKey and RowKey are for in Windows Azure Table Storage,尤其是当您查看这张图片时:

Based on the size and load of a partition, partitions are fanned out across machines. Whenever a partition gets a high load or grows in size, the Windows Azure storage management can kick in and move a partition to another machine:

编辑:
如果您想通过多种方式查询数据,请考虑以多种方式存储它们。特别是因为存储很便宜,所以多次存储数据并不是那么糟糕。这样你就可以优化阅读。这就是所谓的 Materialized View pattern 可以 "help support efficient querying and data extraction, and improve application performance"。

但是,您应该记住,这对于静态数据来说很简单。如果您的数据变化很大,在多次存储时保持它们同步可能会很麻烦。

rickvdbosch 的回答很准确。

假设这是一个应用程序,这里有一些额外的想法。一种方法是并行读取较小的 PartitionKey 范围。例如,假设正在处理的范围是 June/2018,我们将有:

  • Thread-1 => PartitionKey ge '20180601' && PartitionKey lt '20180605'
  • Thread-2 => PartitionKey ge '20180605' && PartitionKey lt '20180610'
  • Thread-3 => PartitionKey ge '20180610' && PartitionKey lt '20180615'
  • Thread-4 => PartitionKey ge '20180615' && PartitionKey lt '20180620'
  • Thread-5 => PartitionKey ge '20180620' && PartitionKey lt '20180725'
  • Thread-6 => PartitionKey ge '20180625' && PartitionKey lt '20180701'

此外,在不使用 TableQuery 结构的情况下,可以更积极地并行读取较小的分区(例如每天)。

请注意,上述两种方法都无法处理高度不平衡的分区策略。例如,假设 June/2018 的 95% 的数据存储在“20180605”到“20180610”的范围内或在一天内,与串行相比,整体执行时间可能会或可能不会被察觉到改进在这种情况下阅读,特别是因为并行开销(例如线程、内存分配、同步等)。

现在,假设这是在 Windows OS 上 运行ning 的 .NET 应用程序,并且上述方法对您的场景很有吸引力 ,考虑:

  • 增加最大连接数;
  • 禁用 Nagle 算法;

在下面找到一个代码片段来更改应用程序配置。请注意:

  • 可以为 maxconnection 定义地址(例如 https://whosebug.com)而不是使用 "*".
  • 建议进行 运行 性能测试,以便在发布到生产环境之前对 maxconnection 的适当配置进行基准测试。

https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/network/connectionmanagement-element-network-settings 找到有关连接管理的更多详细信息。