13

这是一段初始化 TableBatchOperation 的代码,该操作旨在一次批处理中检索两行:

 TableBatchOperation batch = new TableBatchOperation();
 batch.Add(TableOperation.Retrieve("somePartition", "rowKey1"));
 batch.Add(TableOperation.Retrieve("somePartition", "rowKey2")); 
 //second call throws an ArgumentException:
 //"A batch transaction with a retrieve operation cannot contain 
 //any other operation"

如前所述,抛出异常,并且似乎不支持在单个批次中检索 N 行。这对我来说很重要,因为每个请求我需要检索大约 50 行。这个问题在性能方面与成本方面一样重要。您可能知道,Azure 表存储定价基于事务量,这意味着 50 次检索操作的成本是单个批处理操作的 50 倍。

我错过了什么吗?

旁注 我正在使用新的 Azure Storage api 2.0。我注意到这个问题从未在网络上提出过。这个约束可能是最近添加的?

编辑

我在这里找到了一个相关的问题:Very Slow on Azure Table Storage Query on PartitionKey/RowKey List。似乎在行键上使用带有“或”的 TableQuery 将导致全表扫描。这里真的有一个很严重的问题...

4

7 回答 7

5

在 Azure 表存储 (ATS) 中设计分区键 (PK) 和行键 (RK) 方案时,您的主要考虑应该是如何检索数据。正如您所说,您运行的每个查询都会花费金钱,但更重要的是时间,因此您需要在一个有效的查询中获取所有数据。您可以在 ATS 上运行的高效查询属于以下类型:

  • 精确的PK和RK
  • 精确的 PK、RK 范围
  • PK范围
  • PK 范围,RK 范围

根据您的评论,我猜您有一些与此类似的数据:

PK    RK     Data
Guid1 A      {Data:{...}, RelatedRows: [{PK:"Guid2", RK:"B"}, {PK:"Guid3", RK:"C"}]}
Guid2 B      {Data:{...}, RelatedRows: [{PK:"Guid1", RK:"A"}]
Guid3 C      {Data:{...}, RelatedRows: [{PK:"Guid1", RK:"A"}];}

并且您已经在 Guid1 检索到数据,现在您需要加载 Guid2 和 Guid3。我还假设这些行没有共同点,就像它们都是针对同一个用户一样。考虑到这一点,我将创建一个额外的“索引表”,如下所示:

PK      RK      Data
Guid1-A Guid2-B {Data:{....}}
Guid1-A Guid3-C {Data:{....}}
Guid2-B Guid1-A {Data:{....}}
Guid2-B Guid1-A {Data:{....}}

其中PK是父行的组合PK和RK,RK是子行的组合PK和RK。然后,您可以运行一个查询,返回所有带有 PK="Guid1-A" 的行,您只需一次调用(或两次调用)即可获得所有相关数据。这产生的最大开销是在您的写入中,因此现在当您对一行进行正确处理时,您还必须为每个相关行写入行,并确保数据保持最新(这可能不是问题如果这是一种只写一次的场景,对你来说)。

如果我的任何假设是错误的,或者如果您有一些示例数据,我可以使用更多相关示例更新此答案。

于 2013-01-08T03:33:25.577 回答
4

尝试这样的事情:

TableQuery<DynamicTableEntity> query = new TableQuery<DynamicTableEntity>()
                                                .Where(TableQuery.CombineFilters(
                                                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "partition1"),
                                                    TableOperators.And,
                                                    TableQuery.CombineFilters(
                                                        TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "row1"),
                                                        TableOperators.Or,
                                                        TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "row2"))));
于 2013-09-16T20:55:05.687 回答
3

我知道这是一个老问题,但由于 Azure STILL 不支持二级索引,它似乎会在一段时间内相关。

我遇到了同样类型的问题。在我的场景中,我需要在同一个分区中查找数百个项目,其中有数百万行(将 GUID 想象为行键)。我测试了几个选项来查找 10,000 行

  1. (PK&&RK)
  2. (PK && RK1) || (PK & RK2) || ...
  3. PK && (RK1 || RK2 || ... )

我使用的是异步 API,最大并行度为 10 度(最多 10 个未完成的请求)。我还测试了几个不同的批量大小(10 行、50、100)。

Test                        Batch Size  API calls   Elapsed (sec)
(PK && RK)                  1           10000       95.76
(PK && RK1) || (PK && RK2)  10          1000        25.94
(PK && RK1) || (PK && RK2)  50          200         18.35
(PK && RK1) || (PK && RK2)  100         100         17.38
PK && (RK1 || RK2 || … )    10          1000        24.55
PK && (RK1 || RK2 || … )    50          200         14.90
PK && (RK1 || RK2 || … )    100         100         13.43

注意:这些都在同一个分区中 - 只是多个行键。

我很乐意减少 API 调用的数量。但作为一个额外的好处,经过的时间也显着减少,节省了计算成本(至少在我这边!)。

毫不奇怪,100 行的批次提供了最佳的经过性能。显然还有其他性能考虑,尤其是网络使用情况(例如,#1 几乎不使用网络,而其他的则更加努力)

编辑 查询许多行键时要小心。查询存在(或当然)URL 长度限制。如果超出长度,查询仍然会成功,因为服务无法判断 URL 已被截断。在我们的例子中,我们将组合查询长度限制为大约 2500 个字符(URL 编码!)

于 2016-05-29T19:08:36.993 回答
0

每个分区有多少个实体?通过一次检索操作,您可以在每个查询中提取多达 1000 条记录。然后,您可以对内存集进行 Row Key 过滤,并且只需支付 1 次操作费用。

另一种选择是执行行键范围查询以在一次操作中检索部分分区。本质上,您指定要返回的行键的上限和下限,而不是整个分区。

于 2013-01-07T13:59:51.947 回答
0

Azure 表存储不支持批量“获取”操作。支持的操作有:添加、删除、更新和合并。您需要将查询作为单独的请求执行。为了加快处理速度,您可能希望并行执行这些查询。

于 2013-01-06T06:29:08.343 回答
0

您最好的选择是创建一个 Linq/OData 选择查询......它将获取您正在寻找的内容。

为了获得更好的性能,您应该对每个分区进行一次查询并同时运行这些查询。

我没有亲自测试过,但认为它会起作用。

于 2013-01-07T13:13:35.893 回答
0

好的,所以批量检索操作,最好的情况是表查询。不太理想的情况将需要并行检索操作。

根据您的 PK、RK 设计,您可以基于 (PK, RK) 的列表找出您需要执行的最小/最有效的检索/查询操作集。然后,您可以并行获取所有这些内容并整理出客户端的确切答案。

RetrieveIMAO,将方法添加到类中是 Microsoft 的设计失误,TableBatchOperation因为它传达了表存储 API 不支持的语义。

现在,我没有心情写一些超级高效的东西,所以我将把这个超级简单的解决方案留在这里。

var retrieveTasks = new List<Task<TableResult>>();

foreach (var item in list)
{
    retrieveTasks.Add(table.ExecuteAsync(TableOperation.Retrieve(item.pk, item.rk)));
}

var retrieveResults = new List<TableResult>();

foreach (var retrieveTask in retrieveTasks)
{
    retrieveResults.Add(await retrieveTask);
}

此异步代码块将list并行获取实体并将结果以retrieveResults保留顺序存储。如果您需要获取连续范围的实体,则可以使用范围查询来改进这一点。

有一个最佳点(您必须通过测试找到)是查询比特定批次检索所需的更多实体可能更快/更便宜的地方,然后丢弃您不需要的检索结果。

如果您有一个小分区,您可能会从这样的查询中受益:

where pk=partition1 and (rk=rk1 or rk=rk2 or rk=rk3)

如果您的键之间的字典(即排序顺序)距离很大,您可能希望并行获取它们。例如,如果您将字母表存储在表存储中,那么在 fetching 时,最好使用并行检索操作来进行 fetchingaz哪些相距较远的操作ab而哪些相距c较近则最好使用查询来进行。获取ab cz将受益于混合方法。

如果您事先了解所有这些,您可以计算出在给定一组 PK 和 RK 的情况下最好的做法是什么。您对基础数据的排序方式了解得越多,您的结果就会越好。我会建议一种通用的方法,而是尝试应用您从这些不同的查询模式中学到的知识来解决您的问题。

于 2018-01-30T18:22:26.317 回答