如何在Azure表存储中使用partitionkey加快查询速度

我们如何提高查询速度?

我们在1-2 minutes范围内大约有 100个消费者,它们执行以下查询。这些运行中的每个运行都代表一个消耗函数的运行。

        TableQuery<T> treanslationsQuery = new TableQuery<T>()
         .Where(
          TableQuery.CombineFilters(
            TableQuery.GenerateFilterCondition("PartitionKey",QueryComparisons.Equal,sourceDestinationPartitionKey),TableOperators.Or,TableQuery.GenerateFilterCondition("PartitionKey",anySourceDestinationPartitionKey)
          )
         );

此查询将产生大约 5000个结果。

完整代码:

    public static async Task<IEnumerable<T>> ExecuteQueryAsync<T>(this CloudTable table,TableQuery<T> query) where T : ITableEntity,new()
    {
        var items = new List<T>();
        TableContinuationToken token = null;

        do
        {
            TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync(query,token);
            token = seg.ContinuationToken;
            items.AddRange(seg);
        } while (token != null);

        return items;
    }

    public static IEnumerable<Translation> Get<T>(string sourceParty,string destinationParty,string wildcardSourceParty,string tableName) where T : ITableEntity,new()
    {
        var acc = CloudStorageaccount.Parse(Environment.GetEnvironmentVariable("conn"));
        var tableclient = acc.CreatecloudTableclient();
        var table = tableclient.GetTableReference(Environment.GetEnvironmentVariable("TableCache"));
        var sourceDestinationPartitionKey = $"{sourceParty.ToLowerTrim()}-{destinationParty.ToLowerTrim()}";
        var anySourceDestinationPartitionKey = $"{wildcardSourceParty}-{destinationParty.ToLowerTrim()}";

        TableQuery<T> treanslationsQuery = new TableQuery<T>()
         .Where(
          TableQuery.CombineFilters(
            TableQuery.GenerateFilterCondition("PartitionKey",anySourceDestinationPartitionKey)
          )
         );

        var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
        return over1000Results.Where(x => x.expireAt > DateTime.Now)
                           .Where(x => x.effectiveAt < DateTime.Now);
    }

在这些执行期间,当有100个使用者时,如您所见,请求将聚集并形成峰值:

如何在Azure表存储中使用partitionkey加快查询速度

在这些高峰期间,请求通常需要1分钟以上的时间:

如何在Azure表存储中使用partitionkey加快查询速度

我们如何提高查询速度?

lingxueer1 回答:如何在Azure表存储中使用partitionkey加快查询速度

  var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
        return over1000Results.Where(x => x.expireAt > DateTime.Now)
                           .Where(x => x.effectiveAt < DateTime.Now);

这是问题之一,您正在运行查询,然后使用这些“ wheres”从内存中对其进行过滤。在查询运行之前将过滤器移至,这会很有帮助。

第二,您必须提供一些行数限制才能从数据库中检索

,

您可以考虑以下三件事:

1 。首先,摆脱对查询结果执行的Where子句。最好在查询中尽可能包含子句(如果您的表上也包含任何索引,则更好)。现在,您可以按以下方式更改查询:

var translationsQuery = new TableQuery<T>()
.Where(TableQuery.CombineFilters(
TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey",QueryComparisons.Equal,sourceDestinationPartitionKey),TableOperators.Or,TableQuery.GenerateFilterCondition("PartitionKey",anySourceDestinationPartitionKey)
    ),TableOperators.And,TableQuery.CombineFilters(
    TableQuery.GenerateFilterConditionForDate("affectiveAt",QueryComparisons.LessThan,DateTime.Now),TableQuery.GenerateFilterConditionForDate("expireAt",QueryComparisons.GreaterThan,DateTime.Now))
));

因为要检索的数据量很大,所以最好并行运行查询。因此,您应将我在{strong> do while 方法中使用的 ExecuteQueryAsync 循环替换为 Parallel.ForEach {3}};这样可以减少查询执行时间。这是一个不错的选择,因为您可以在调用此方法时删除Result,但这有一点限制,我将在这部分代码之后再讨论它:

public static IEnumerable<T> ExecuteQueryAsync<T>(this CloudTable table,TableQuery<T> query) where T : ITableEntity,new()
{
    var items = new List<T>();
    TableContinuationToken token = null;

    Parallel.ForEach(new InfinitePartitioner(),(ignored,loopState) =>
    {
        TableQuerySegment<T> seg = table.ExecuteQuerySegmented(query,token);
        token = seg.ContinuationToken;
        items.AddRange(seg);

        if (token == null) // It's better to change this constraint by looking at https://www.vivien-chevallier.com/Articles/executing-an-async-query-with-azure-table-storage-and-retrieve-all-the-results-in-a-single-operation
            loopState.Stop();
    });

    return items;
}

然后您可以使用 Get 方法调用它:

return table.ExecuteQueryAsync(translationsQuery).Cast<Translation>();

如您所见,方法本身不是异步的(您应该更改其名称),并且 Parallel.ForEach 与传入异步方法不兼容。这就是为什么我改用 ExecuteQuerySegmented 的原因。但是,要使其性能更高并利用异步方法的所有优点,您可以将{>中的 ForEach 循环替换为{{ 3}}或来自Stephen Toub Parallel.While ActionBlock 扩展方法。

2 。执行独立的并行查询然后合并结果是一个不错的选择,即使其性能最多提高10%。这使您有时间能够找到最佳的性能友好查询。但是,永远不要忘记将所有约束都包括在其中,并测试两种方法以了解哪种更适合您的问题。

3 。我不确定这是否是个好建议,但可以这样做并查看结果。如Dataflow中所述:

  

Table服务强制服务器超时,如下所示:

     
      
  • 查询操作:在超时间隔内,查询可能针对   最多五秒钟。如果查询未在以下时间内完成   在五秒钟的时间间隔内,响应包括连续令牌   用于在后续请求中检索剩余项目。查看查询   超时和分页以获取更多信息。

  •   
  • 插入,更新和删除操作:最大超时间隔为   30秒。三十秒也是所有设备的默认间隔   插入,更新和删除操作。

  •   
     

如果您指定的超时时间小于服务的默认超时时间,则会使用您的超时间隔。

因此,您可以尝试超时并检查性能是否有改善。

,

不幸的是,下面的查询引入了全表扫描

    TableQuery<T> treanslationsQuery = new TableQuery<T>()
     .Where(
      TableQuery.CombineFilters(
        TableQuery.GenerateFilterCondition("PartitionKey",anySourceDestinationPartitionKey)
      )
     );

您应该将其分成两个分区键过滤器并分别查询,这将成为两次分区扫描,并且执行效率更高。

,

因此,秘密不仅在代码中,而且在设置Azure存储表中。

a)在Azure中优化查询的主要方法之一就是引入缓存。这将大大减少您的总体响应时间,从而避免您提到的高峰时段的瓶颈。

b)此外,在Azure之外查询实体时,最快的方法是使用PartitionKey和RowKey。这些是表存储中唯一的索引字段,使用这两个字段的任何查询都将在几毫秒内返回。因此,请确保同时使用PartitionKey和RowKey。

在此处查看更多详细信息: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query

希望这会有所帮助。

,

注意:这是常规的数据库查询优化建议。

ORM可能在做一些愚蠢的事情。在进行优化时,可以降低抽象层。因此,我建议用查询语言(SQL?)重写查询,以便更轻松地了解正在发生的事情,并且也更易于优化。

优化查找的关键是排序!与对每个查询扫描整个表相比,对表进行排序通常要便宜得多!因此,如果可能,请按查询中使用的键对表进行排序。在大多数数据库解决方案中,这是通过创建索引键来实现的。

如果组合很少,另一种有效的策略是使每个查询作为始终保持最新状态的单独的(内存中的临时)表。因此,当插入某些内容时,它也会“插入”到“视图”表中。一些数据库解决方案将此称为“视图”。

一种更为粗暴的策略是创建只读副本以分配负载。

本文链接:https://www.f2er.com/3163487.html

大家都在问