C# 如何优化完整表更新的性能

C# 如何优化完整表更新的性能,c#,linq,entity-framework,linq-to-entities,plinq,C#,Linq,Entity Framework,Linq To Entities,Plinq,我正在写一个以斯坦福大学为中心的大型服务Folding@Home项目项目的这一部分是托管在Windows服务中的WCF服务。有了适当的数据库索引和双核Core2Duo/7200rpm磁盘,我可以每秒运行大约1500行(SQL 2012数据中心实例)。当我运行此更新时,每小时都要花相当长的时间遍历所有150万用户,并在必要时添加更新 查看SQL Server Management Studio 2012中的性能分析器,我发现每个用户都是通过单独的查询加载的。EF有没有一种方法可以急切地加载一组给定

我正在写一个以斯坦福大学为中心的大型服务Folding@Home项目项目的这一部分是托管在Windows服务中的WCF服务。有了适当的数据库索引和双核Core2Duo/7200rpm磁盘,我可以每秒运行大约1500行(SQL 2012数据中心实例)。当我运行此更新时,每小时都要花相当长的时间遍历所有150万用户,并在必要时添加更新

查看SQL Server Management Studio 2012中的性能分析器,我发现每个用户都是通过单独的查询加载的。EF有没有一种方法可以急切地加载一组给定大小的用户,在内存中进行更新,然后保存更新后的用户-使用比单一选择、单一更新更优雅的查询?我目前正在使用EF5,但如果我需要移动到6以提高性能,我会。此进程延迟的主要原因是等待数据库结果

另外,如果我对ForAll或pre-processing有任何需要更改的地方,请随时提及。通过控制每个EF上下文的大小,组预处理非常快速,并且显著提高了更新的速度-但是如果我可以预处理更多的内容并提高总体时间,我非常愿意研究它

private void DoUpdate(IEnumerable<Update> table)
{
    var t = table.ToList();
    var numberOfRowsInGroups = t.Count() / (Properties.Settings.Default.UpdatesPerContext); //Control each local context size.  120 works well on most systems I have.

    //Split work groups out of the table of updates.
    var groups = t.AsParallel()
                    .Select((update, index) => new {Value = update, Index = index})
                    .GroupBy(a => a.Index % numberOfRowsInGroups)
                    .ToList();

    groups.AsParallel().ForAll(group =>
    {
        var ents = new FoldingDataEntities();
        ents.Configuration.AutoDetectChangesEnabled = false;
        ents.Configuration.LazyLoadingEnabled = true;
        ents.Database.Connection.Open();

        var count = 0;
        foreach (var a in group)
        {
            var update = a.Value;
            var data = UserData.GetUserData(update.Name, update.Team, ents); //(Name,Team) is a superkey; passing ents allows external context control

            if (data.TotalPoints < update.NewCredit)
            {
                data.addUpdate(update.NewCredit, update.Sum); //basic arithmetic, very quick - may attach a row to the UserData.Updates collection. (does not SaveChanges here)
            }
        }

        ents.ChangeTracker.DetectChanges();
        ents.SaveChanges();
    });
}

//from the UserData class which wraps the EF code.
public static UserData GetUserData(string name, long team, FoldingDataEntities ents)
{
    return context.Users.Local.FirstOrDefault(u => (u.Team == team && u.Name == name))
        ?? context.Users.FirstOrDefault(u => (u.Team == team && u.Name == name))
        ?? context.Users.Add(new User { Name = name, Team = team, StartDate = DateTime.Now, LastUpdate = DateTime.Now });
}

internal struct Update
{
    public string Name;
    public long NewCredit;
    public long Sum;
    public long Team;
}
private void DoUpdate(IEnumerable表)
{
var t=table.ToList();
var numberOfRowsInGroups=t.Count()/(Properties.Settings.Default.UpdatesPerContext);//控制每个本地上下文大小。120适用于我拥有的大多数系统。
//从更新表中拆分工作组。
变量组=天门冬氨酸杆菌()
.Select((更新,索引)=>new{Value=update,index=index})
.GroupBy(a=>a.索引%numberOfRowsInGroups)
.ToList();
groups.AsParallel().ForAll(组=>
{
var ents=新的FoldingDataEntities();
ents.Configuration.AutoDetectChangesEnabled=false;
ents.Configuration.LazyLoadingEnabled=true;
ents.Database.Connection.Open();
var计数=0;
foreach(组中的var a)
{
var更新=a.值;
var data=UserData.GetUserData(update.Name,update.Team,ents);/(Name,Team)是一个超键;传递ents允许外部上下文控制
如果(data.TotalPoints(u.Team==Team&&u.Name==Name))
??context.Users.FirstOrDefault(u=>(u.Team==团队和u.Name==名称))
??context.Users.Add(新用户{Name=Name,Team=Team,StartDate=DateTime.Now,LastUpdate=DateTime.Now});
}
内部结构更新
{
公共字符串名称;
公共长期信贷;
公共长期基金;
公共长队;
}

EF不是原始性能的解决方案。。。这是创建数据访问层(DAL)的“简单方法”,但会带来相当大的开销。我强烈建议使用Dapper或原始ADO.NET进行批量更新。。。会快得多


现在,为了回答您的问题,要在EF中进行批量更新,您需要下载一些扩展和第三方插件来启用这些功能。请参阅:

谢谢您的回复!我将研究ADO.NET。我创建了UserData类,以便在需要时可以交换关系模型。也许我会把它变成一个接口,实现一个EF5UserData、一个EF6UserData和一个ADOUserData,并分析这个应用程序中比较真实的性能。我很欣赏这令人深思的东西。