EF Core 8.0.17中Skip/Take分页查询超时的性能问题求助
EF Core 8.0.17中Skip/Take分页查询超时的性能问题求助
各位大佬,我碰到了一个EF Core 8.0.17的诡异性能问题,想请教下大家:
我做了一个文档元数据的查询功能,用EF.Functions.Contains调用全文索引搜索文档内容。现在的情况是:
- 当我给查询加上
Skip/Take做分页时,请求直接超时了 - 但如果去掉分页逻辑,直接执行整个查询,虽然慢一点但最终能返回结果
- 更奇怪的是,我把EF生成的SQL拿出来在SSMS里直接执行,居然不到1秒就跑完了
我的查询构建代码(DocumentRepository.cs)
var documents = _context.Document .Include(d => d.DocumentType).AsSplitQuery().AsNoTracking() .Include(d => d.DocumentStatus).AsSplitQuery().AsNoTracking() .Include(d => d.OriginationType).AsSplitQuery().AsNoTracking() .Where(d => d.DeleteDate == null) .AsNoTracking() .AsQueryable(); if (!string.IsNullOrEmpty(documentSearchParams.DocumentText)) { documents = documents.Where(d => EF.Functions.Contains(d.DocumentText, searchCondition.ToString())); } if (!string.IsNullOrEmpty(documentSearchParams.DocumentTitle)) { documents = documents.Where(d => d.Title.Contains(documentSearchParams.DocumentTitle)); } // 其他搜索参数的过滤逻辑... // 设置排序 documents = documents.OrderBy(d => d.CreateDate); // 只选择返回结果需要的字段 var d = documents.Select(doc => new Document() { DocumentId = doc.DocumentId, PublicationDate = doc.PublicationDate, Title = doc.Title, DocumentNumber = doc.DocumentNumber, Author = doc.Author, DocumentType = doc.DocumentType, DocumentTypeId = doc.DocumentTypeId, DocumentStatus = doc.DocumentStatus, DocumentStatusId = doc.DocumentStatusId, Summary = doc.Summary, Keywords = doc.Keywords, FileName = doc.FileName, DocumentPersons = doc.DocumentPersons, CreatedPerson = doc.CreatedPerson, CreatedPersonId = doc.CreatedPersonId, CreateDate = doc.CreateDate, UpdateDate = doc.UpdateDate, UpdatedPersonId = doc.UpdatedPersonId, UpdatedPerson = doc.UpdatedPerson }); return await PagedList<Document>.CreateAsync(d, documentSearchParams.PageNumber, documentSearchParams.PageSize);
分页逻辑代码(PagedList.cs)
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize) { var count = await source.CountAsync(); var items = new List<T>(); if (count > pageSize) { var numberToSkip = pageSize * pageNumber; source = source .Skip(numberToSkip) .Take(pageSize); items = await source.ToListAsync(); } else { items = await source.ToListAsync(); } return new PagedList<T>(items, count, pageNumber, pageSize); }
EF生成的SQL片段
DECLARE @__8__locals1_documentSearchParams_DocumentText_0_contains nvarchar(4000) = N'%test%'; DECLARE @__Trim_1_contains nvarchar(4000) = N'%test%'; DECLARE @__ToString_3 nvarchar(4000) = N'"*test*"'; DECLARE @__p_4 int = 0; DECLARE @__p_5 int = 3; SELECT [t].[DocumentId], [t].[PublicationDate], [t].[Title], [DocumentNumber], [t].[Author], [d0].[DocumentTypeId], [d0].[Code], [d0].[CreateDate], [d0].[CreatedPersonId], [d0].[DeleteDate], [d0].[DeletedPersonId], [d0].[IsActive], [d0].[Name], [d0].[SortOrder], [d0].[UpdateDate], [d0].[UpdatedPersonId], [t].[DocumentTypeId], [d1].[DocumentStatusId], [d1].[Description], [d1].[SortOrder], [d1].[StatusCode], [t].[DocumentStatusId], [t].[Summary], [t].[Keywords], [t].[FileName], [t].[MissionAreaId], [p].[PersonId], [p0].[PersonId], [d3].[DocumentId], [d3].[PersonId], [p].[DomainName], [p].[Email], [p].[FirstName], [p].[LastName], [p].[ObjectName], [t].[CreatedPersonId], [t].[CreateDate], [t].[UpdateDate], [t].[UpdatedPersonId], [p0].[DomainName], [p0].[Email], [p0].[FirstName], [p0].[LastName], [p0].[ObjectName] FROM ( SELECT [d].[DocumentId], [d].[Author], [d].[AuthorIdList], [d].[Budget], [d].[ClassificationTypeId], [d].[ClassifiedTitle], [d].[CreateDate], [d].[CreatedPersonId], [d].[DocumentNumber], [d].[DocumentStatusId], [d].[DocumentTypeId], [d].[FileName], [d].[IsProtected], [d].[Keywords], [d].[MissionAreaId], [d].[OriginatingGroup], [d].[PublicationDate], [d].[ReviewId], [d].[SortingTitle], [d].[Summary], [d].[Title], [d].[UpdateDate], [d].[UpdatedPersonId] FROM [Document] AS [d] WHERE [d].[Title] LIKE @__8__locals1_documentSearchParams_DocumentText_0_contains ESCAPE N'\' OR [d].[DocumentNumber] LIKE @__Trim_1_contains ESCAPE N'\' OR CONTAINS([d].[DocumentText], @__ToString_3) OR LOWER([d].[Summary]) LIKE @__8__locals1_documentSearchParams_DocumentText_0_contains ESCAPE N'\' OR [d].[Author] LIKE @__8__locals1_documentSearchParams_DocumentText_0_contains ESCAPE N'\' OR [d].[Title] LIKE @__8__locals1_documentSearchParams_DocumentText_0_contains ESCAPE N'\' ORDER BY [d].[PublicationDate] DESC OFFSET @__p_4 ROWS FETCH NEXT @__p_5 ROWS ONLY ) AS [t] LEFT JOIN [DocumentType] AS [d0] ON [t].[DocumentTypeId] = [d0].[DocumentTypeId] LEFT JOIN [DocumentStatus] AS [d1] ON [t].[DocumentStatusId] = [d1].[DocumentStatusId] INNER JO
(注:SQL内容被截断,核心分页逻辑已包含在子查询中)
现在我特别困惑:为什么EF执行带Skip/Take的查询会超时,但直接跑生成的SQL却这么快?有没有什么EF Core的配置或者查询写法的坑我没注意到?比如AsSplitQuery、AsNoTracking的使用是否有问题?或者是EF处理分页时产生了额外的性能开销?
希望有遇到过类似问题的大佬能给点思路,谢谢啦!
内容来源于stack exchange
相关产品推荐
相关产品推荐

