我向数据量很大的表写入查询速度是否正常?

问题描述 投票:0回答:1

我有一个名为“申请”的表,用于跟踪候选人对职位空缺提出的申请。现在,我需要为 HR 编写一个统计查询,当 HR 登录时,我应该带上申请该 HR 创建的职位空缺的候选人。由于我的查询中同时存在过滤器,因此我还必须包含与候选人相关的其他表。然而,我的查询速度很慢,并且消耗了大量的 RAM。我的 16 GB RAM Linux 服务器在运行该项目 1-2 小时后就被填满并崩溃了。当我检查服务器时,我发现仅这个查询就不断消耗 RAM。您认为我可以如何优化这个,或者查询可能有什么问题?此外,还有+400,000份申请和+100,000名候选人

var entities = _context.Applications
    .Include(p => p.Candidate)
    .ThenInclude(p => p.CandidateIndustryFields)
    .ThenInclude(p => p.DesiredField)
    .Include(p => p.Vacancy)
    .Include(p => p.Candidate)
    .ThenInclude(p => p.CandidateIndustrySubFields)
    .ThenInclude(p => p.SubField)
    .Include(p => p.Candidate)
    .ThenInclude(x => x.CandidateSkills)
    .ThenInclude(x => x.Skill)
    .Include(p => p.Candidate)
    .ThenInclude(x => x.CandidateBackgrounds)
    .ThenInclude(x => x.WorkExperienceType)
    .Where(m => (m.Vacancy.RecruiterId == userId) && (m.StatusId == searchParameter.StatusId || searchParameter.StatusId == null)
        && (string.IsNullOrEmpty(searchParameter.Name) || m.Candidate.Name.Contains(searchParameter.Name))
        && (string.IsNullOrEmpty(searchParameter.Surname) || m.Candidate.Surname.Contains(searchParameter.Surname))
        && (string.IsNullOrEmpty(searchParameter.PassportFin) || m.Candidate.CandidatePassports.Any(p => p.PassportFin == searchParameter.PassportFin.ToUpper()))
        && (string.IsNullOrEmpty(searchParameter.Patronymic) || m.Candidate.FatherName.Contains(searchParameter.Patronymic))
        && (searchParameter.Gender == null || m.Candidate.GenderId == searchParameter.Gender)
        && (searchParameter.MaritalStatus == null || m.Candidate.MaritalStatusId == searchParameter.MaritalStatus)
        && (searchParameter.MilitaryStatus == null || m.Candidate.MillitaryStatusId == searchParameter.MilitaryStatus)
        && (searchParameter.HasPhoto == null || (searchParameter.HasPhoto == 0 ? m.Candidate.CandidateImage == null : m.Candidate.CandidateImage != null))
        && (searchParameter.MinBirthDate == null || m.Candidate.BirthDate >= searchParameter.MinBirthDate)
        && (searchParameter.MaxBirthDate == null || m.Candidate.BirthDate <= searchParameter.MaxBirthDate)
        && (string.IsNullOrEmpty(searchParameter.Address) || m.Candidate.CandidateContactInfo.ActualAddress.Contains(searchParameter.Address) || m.Candidate.CandidateContactInfo.RegisteredAddress.Contains(searchParameter.Address))
        && (string.IsNullOrEmpty(searchParameter.Email) || m.Candidate.User.Email.Contains(searchParameter.Email))
        && (regions.Count == 0 || regions.Contains(m.Candidate.CandidateContactInfo.RegionId.Value))
        && (districts.Count == 0 || districts.Contains(m.Candidate.CandidateContactInfo.DistrictId.Value))
        && (educationDegrees.Count == 0 || m.Candidate.CandidateEducations.Any(k => educationDegrees.Contains(k.DegreeId)) || m.Candidate.CandidateCustomEducations.Any(l => educationDegrees.Contains(l.DegreeId)))
        && (searchParameter.EntranceScoreMin == null || m.Candidate.CandidateEducations.Any(k => k.EntrancePoint >= searchParameter.EntranceScoreMin) || m.Candidate.CandidateCustomEducations.Any(l => l.EntrancePoint >= searchParameter.EntranceScoreMin))
        && (searchParameter.EntranceScoreMax == null || m.Candidate.CandidateEducations.Any(k => k.EntrancePoint <= searchParameter.EntranceScoreMax) || m.Candidate.CandidateCustomEducations.Any(l => l.EntrancePoint <= searchParameter.EntranceScoreMax))
        && (string.IsNullOrEmpty(searchParameter.Profession) || m.Candidate.CandidateCustomEducations.Any(p => p.Profession.Contains(searchParameter.Profession)) || m.Candidate.CandidateEducations.Any(p => p.Profession.Contains(searchParameter.Profession)))
        && (string.IsNullOrEmpty(searchParameter.Position) || m.Candidate.CandidateBackgrounds.Any(k => k.Position.Contains(searchParameter.Position)))
        && (string.IsNullOrEmpty(searchParameter.CompanyName) || m.Candidate.CandidateBackgrounds.Any(k => k.CompanyName.Contains(searchParameter.CompanyName)))
        && (computerSkills.Count == 0 || m.Candidate.ComputerSkills.Any(l => computerSkills.Contains(l.SkillId)))
        //&& (languageSkills.Count == 0 || m.Candidate.CandidateLanguages.Any(l => languageSkills.Any(c => c.LanguageId == l.LanguageId && c.Writing >= l.Writing && c.Reading >= l.Reading && c.Speaking >= l.Speaking)))
        && (string.IsNullOrEmpty(searchParameter.Phone) || m.Candidate.CandidateMobilPhones.Any(l => l.PhoneNumber.Contains(searchParameter.Phone)))
        && (string.IsNullOrEmpty(searchParameter.CertificateName) || m.Candidate.CandidateCertificates.Any(n => n.Name.Contains(searchParameter.CertificateName)))
        && (string.IsNullOrEmpty(searchParameter.Organization) || m.Candidate.CandidateCertificates.Any(n => n.IssuedBy.Contains(searchParameter.Organization)))
        && (fields.Count == 0 || m.Candidate.CandidateIndustryFields.Any(l => fields.Contains(l.DesiredFieldId)))
        && (skills.Count == 0 || m.Candidate.CandidateSkills.Any(l => skills.Contains(l.SkillId)))
        && (workExperiences.Count == 0 || m.Candidate.CandidateBackgrounds.Any(l => workExperiences.Contains(l.WorkExperienceId ?? -1)))
        && (searchParameter.HasExperience == null || (!searchParameter.HasExperience.Value ? m.Candidate.CandidateBackgrounds.Count == 0 : m.Candidate.CandidateBackgrounds.Count != 0))
        && (searchParameter.GeneralSearch == null || m.Candidate.Name.Contains(searchParameter.GeneralSearch)
                || m.Candidate.Surname.Contains(searchParameter.GeneralSearch)
                || m.Candidate.FatherName.Contains(searchParameter.GeneralSearch)
                || m.Candidate.CandidateContactInfo.ActualAddress.Contains(searchParameter.GeneralSearch)
                || m.Candidate.CandidateContactInfo.RegisteredAddress.Contains(searchParameter.GeneralSearch)
                || m.Candidate.User.Email.Contains(searchParameter.GeneralSearch)
                || m.Candidate.CandidateCustomEducations.Any(p => p.InstitutionName.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateCustomEducations.Any(p => p.Profession.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateEducations.Any(p => p.Profession.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateBackgrounds.Any(p => p.Position.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateBackgrounds.Any(p => p.CompanyName.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateMobilPhones.Any(p => p.PhoneNumber.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateCertificates.Any(p => p.Name.Contains(searchParameter.GeneralSearch))
                || m.Candidate.CandidateCertificates.Any(p => p.IssuedBy.Contains(searchParameter.GeneralSearch))));
mysql performance asp.net-core asp.net-web-api entity-framework-core
1个回答
0
投票

您可以做很多事情。你需要循序渐进地工作,并期望有时会做一些让事情变得更糟的事情。数据库查询执行引擎很复杂,它们的行为可能是意外的(例如,我对查询做了一次小小的更改,它的速度快了约 100 倍!)。你还有很多东西要学。

所有这些都可能成为解决方案的一部分。

First:使用EF的日志功能来捕获SQL和参数。然后手动执行以 (1) 查看数据库方面需要多长时间,& (2) 使用

EXPLAIN
查看数据库如何执行查询:花时间了解结果。

您将需要它来识别要添加的索引(并评估对 SQL 的更改)。

第二:如果您不需要使用更改跟踪来允许设置属性然后保存更改:避免

Include
ThenInclude
:改为使用
join
并投影结果(
select
子句)。这还应该允许您避免从所有表中获取所有数据:只需获取您需要的数据(可能节省大量内存)。请记住,当您需要集合时,可以将查询表达式放入投影中。

如果这是一个查询页面,这似乎是可能的。

另外,使用

AsNoTracking()
! (参见此处。)

第三:替换

&& (string.IsNullOrEmpty(searchParameter.Name) || m.Candidate.Name.Contains(searchParameter.Name))

var query = /* everything before this point */

if (!string.IsNullOrEmpty(searchParameter.Name) {
  query = query.Where(x => x.Candidate.Name.Contains(searchParameter.Name));
}

仅构建会改变结果的条件。

这将为数据库节省不需要在数据库上完成的工作,并且允许这些子句sargable

(对于一组或条件,您还可以使用

Expression
类型手动查看来构建表达式树,因为您不能仅将一个分层到另一个上。)

最后:LINQ to SQL 很棒......直到查询开始真正完成,此时您最好编写自己的 SQL 来提供完全控制(并且还可以访问 LINQ 中未公开的数据库查询功能)。

不要陷入沉没成本谬误:准备好放弃只会导致死胡同的工作。

© www.soinside.com 2019 - 2024. All rights reserved.