使用分页优化的 Linq to SQL 查询
Linq to SQL query with paging optimization
我有一个查询,它从包含日志记录数据的 table 中选择条目。 table 包含大约 100 万个数据集。问题是这个查询需要很长时间才能完成。我尝试了查询的一些变体,但性能仍然很差,尤其是最后的页面。
Expression<Func<EventLogEntry, bool>> search = c => true;
if (!string.IsNullOrEmpty(param.sSearch))
{
search = c => c.Message.Contains(param.sSearch) ||
c.Source.Contains(param.sSearch) ||
c.Category.Contains(param.sSearch);
}
using (DbContext db = new DbContext())
{
logs = db.Logs
.Where(search)
.OrderByDescending(x => x.Timestamp)
.Skip(param.iDisplayStart)
.Take(param.iDisplayLength)
.ToArray();
}
即使没有搜索词,查询也至少需要 10 秒。生成的 SQL 如下所示:
SELECT TOP (10)
[Extent1].[EventId] AS [EventId],
[Extent1].[Id] AS [Id],
[Extent1].[EntryType] AS [EntryType],
[Extent1].[Message] AS [Message],
[Extent1].[Category] AS [Category],
[Extent1].[Source] AS [Source],
[Extent1].[Timestamp] AS [Timestamp],
[Extent1].[Server_Id] AS [Server_Id]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[EntryType] AS [EntryType], [Extent1].[Message] AS [Message], [Extent1].[EventId] AS [EventId], [Extent1].[Category] AS [Category], [Extent1].[Source] AS [Source], [Extent1].[Timestamp] AS [Timestamp], [Extent1].[Server_Id] AS [Server_Id], row_number() OVER (ORDER BY [Extent1].[Timestamp] DESC) AS [row_number]
FROM [dbo].[EventLogEntries] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 0
ORDER BY [Extent1].[Timestamp] DESC
有什么方法可以加快这个查询的速度吗?
指数是您的朋友。评论中对此进行了讨论,但我会添加更多细节而不是僵尸问题。
因为排序是在Timestamp
的基础上进行的,而且跳取又与顺序有关,所以在Timestamp
上加一个索引可以让服务器加快排序速度和分页。
用于过滤或连接的任何列的索引通常也有帮助,但对基于 Contains
的过滤器帮助不大。
如果您稍后执行 Select()
以便最终只使用某些列,那么带有 Using
的索引会有所帮助,例如如果你有
`….Select(l => new {l.Id, l.EntryType, l.Message})`
然后 Timestamp
上使用 INCLUDE(Id, EntryType, Message)
的索引将允许仅从索引完成查询。但是,当所有列都被使用时,这并没有太大好处,因此您只会得到使用大量 space 并减慢插入速度的大型索引的缺点。因此,它在这里没有价值,但值得了解它如何使类似案例受益。
同样,如果可能,请避免使用 ToArray()
,而是在结果出现时对其进行处理。这提供了更快的获得第一个结果的时间、更快的总时间和更少的内存使用。
我有一个查询,它从包含日志记录数据的 table 中选择条目。 table 包含大约 100 万个数据集。问题是这个查询需要很长时间才能完成。我尝试了查询的一些变体,但性能仍然很差,尤其是最后的页面。
Expression<Func<EventLogEntry, bool>> search = c => true;
if (!string.IsNullOrEmpty(param.sSearch))
{
search = c => c.Message.Contains(param.sSearch) ||
c.Source.Contains(param.sSearch) ||
c.Category.Contains(param.sSearch);
}
using (DbContext db = new DbContext())
{
logs = db.Logs
.Where(search)
.OrderByDescending(x => x.Timestamp)
.Skip(param.iDisplayStart)
.Take(param.iDisplayLength)
.ToArray();
}
即使没有搜索词,查询也至少需要 10 秒。生成的 SQL 如下所示:
SELECT TOP (10)
[Extent1].[EventId] AS [EventId],
[Extent1].[Id] AS [Id],
[Extent1].[EntryType] AS [EntryType],
[Extent1].[Message] AS [Message],
[Extent1].[Category] AS [Category],
[Extent1].[Source] AS [Source],
[Extent1].[Timestamp] AS [Timestamp],
[Extent1].[Server_Id] AS [Server_Id]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[EntryType] AS [EntryType], [Extent1].[Message] AS [Message], [Extent1].[EventId] AS [EventId], [Extent1].[Category] AS [Category], [Extent1].[Source] AS [Source], [Extent1].[Timestamp] AS [Timestamp], [Extent1].[Server_Id] AS [Server_Id], row_number() OVER (ORDER BY [Extent1].[Timestamp] DESC) AS [row_number]
FROM [dbo].[EventLogEntries] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 0
ORDER BY [Extent1].[Timestamp] DESC
有什么方法可以加快这个查询的速度吗?
指数是您的朋友。评论中对此进行了讨论,但我会添加更多细节而不是僵尸问题。
因为排序是在Timestamp
的基础上进行的,而且跳取又与顺序有关,所以在Timestamp
上加一个索引可以让服务器加快排序速度和分页。
用于过滤或连接的任何列的索引通常也有帮助,但对基于 Contains
的过滤器帮助不大。
如果您稍后执行 Select()
以便最终只使用某些列,那么带有 Using
的索引会有所帮助,例如如果你有
`….Select(l => new {l.Id, l.EntryType, l.Message})`
然后 Timestamp
上使用 INCLUDE(Id, EntryType, Message)
的索引将允许仅从索引完成查询。但是,当所有列都被使用时,这并没有太大好处,因此您只会得到使用大量 space 并减慢插入速度的大型索引的缺点。因此,它在这里没有价值,但值得了解它如何使类似案例受益。
同样,如果可能,请避免使用 ToArray()
,而是在结果出现时对其进行处理。这提供了更快的获得第一个结果的时间、更快的总时间和更少的内存使用。