1

我在本地开发结构中使用 ASP.NET MVC 和 Azure 表存储。处理大型结果集时,我的分页代码非常慢:

var PageSize = 25;
var qResult2 = from c in svc.CreateQuery<SampleEntity>(sampleTableName)
                           where c.PartitionKey == "samplestring"
                           select c;
TableStorageDataServiceQuery<SampleEntity> tableStorageQuery = 
                 new TableStorageDataServiceQuery<SampleEntity>
                 (qResult2 as DataServiceQuery<SampleEntity>);
var result = tableStorageQuery.ExecuteAllWithRetries()
                                .Skip((page - 1) * PageSize)
                                .Take(PageSize);
var numberOfEntities = tableStorageQuery.ExecuteAllWithRetries().Count
ViewData["TotalPages"] = (int)Math.Ceiling((double) numberOfEntities / PageSize);
ViewData["CurrentPage"] = page;
return View(result);

View 使用 ViewData 使用 Sanderson 的 MVC 书中的代码计算分页链接。对于具有 1000 多个实体的 Azure 表,这非常慢。对于初学者来说,“计数”需要相当长的时间来计算实体的总数。如果我正确阅读了我的 LINQ 书,这是因为查询没有实现 ICollection。这本书是 Joseph Rattz 的“Pro LINQ”。

即使我将“numberOfEntities”设置为已知总数(例如 1500),对于 10 以上的页面,分页仍然很慢。我猜 .Skip 和/或 .Take 很慢。另外,我调用了 ExecuteAllWithRetries() 两次,如果实际上 Azure 被查询了两次,那也无济于事。

在使用 ASP.NET MVC 和 Azure 对大型数据集进行分页时,我应该遵循什么策略?

编辑:我不需要知道确切的总页数。

4

1 回答 1

4

Skip并且Take不是这里的问题 - 它们将针对IEnumerable已经在内存中的 执行,因此非常快。

ExecuteAllWithRetries很可能是这里的罪魁祸首 - 您基本上是在此调用中从远程存储中检索分区中的所有实体,这将导致非常大的有效负载。

在表存储中以您显示的方式进行分页非常困难。这里有几个问题:

  • 唯一保证的顺序是PartitionKey/RowKey顺序,因此您需要在设计时考虑到RowKeys这一点。

  • 您可以Take在查询中执行(即您的qResult2),因此这将减少通过网络传输的实体数量。

  • 要执行Skip类似的功能,您需要使用比较运算符。因此,您需要知道您在结果集中的位置并查询RowKeys高于该值的所有内容(即,where c.RowKey > [lastRowKey]在您的查询中添加类似的内容)

  • 如果不自己跟踪计数(或像您已经在做的那样检索整个表),就无法检索计数。根据您的设计,您可以将计数与每个实体一起存储(即,使用递增值) - 但只需确保跟踪并发编辑冲突等。如果您确实跟踪每个实体的计数,那么您也可以Skip使用它来执行您的操作。另一种选择是将计数存储在另一个实体中的单个值中(您可以使用同一个表来确保事务行为)。您实际上也可以组合这些方法(将计数存储在单个实体中,以获得乐观并发,并将其存储在每个实体中,以便您知道它的位置)。

  • 如果可能的话,另一种选择是完全摆脱计数。您会注意到一些大型可扩展站点这样做 - 它们不提供有多少页面的确切列表,但它们可能会让您前进/后退几页。这基本上消除了计数的需要 - 您只需要跟踪RowKeys下一页/上一页。

于 2009-09-21T04:29:57.850 回答