我在本地开发结构中使用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的大型数据集?
编辑:我不需要知道确切的总页数。
答案 0 :(得分:4)
Skip
和Take
不是问题 - 它们将针对IEnumerable
执行,ExecuteAllWithRetries
已经在内存中,因此非常快。
PartitionKey
可能是这里的罪魁祸首 - 您基本上是在此调用中从远程存储中检索分区中的所有实体,这将导致非常大的有效负载。
在表格存储中,以您展示的方式分页非常困难。以下是一些问题:
唯一保证的订单是RowKey
/ RowKeys
订单,因此您需要设计Take
时考虑到这一点。
您可以在查询中执行qResult2
(即您的Skip
),这样就可以减少通过网络传输的实体数量。
要执行类似RowKeys
的功能,您需要使用比较运算符。因此,您需要知道自己在结果集中的位置并查询该值之上的所有where c.RowKey > [lastRowKey]
(例如,在查询中添加Skip
之类的内容)
如果没有自己跟踪它(或者像你已经在做的那样检索整个表格),就无法检索计数。根据您的设计,您可以将计数与每个实体一起存储(即使用递增值) - 但只需确保跟踪并发编辑冲突等。如果您确实跟踪每个实体的计数,那么您也可以使用此功能执行RowKeys
。另一种选择是将计数存储在另一个实体中的单个值中(您可以使用相同的表来确保事务行为)。您实际上也可以组合这些方法(将计数存储在单个实体中,以获得乐观并发,并将其存储在每个实体中,以便您知道它在哪里)。
如果可能的话,另一种方法是完全摆脱计数。您会注意到有几个大型可扩展站点执行此操作 - 它们不提供有多少页面的确切列表,但它们可能会让您前进/后退几页。这基本上消除了计数的需要 - 您只需要跟踪下一个/上一页的{{1}}。