0

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

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

此外,如果我对 ForAll 或预处理有任何需要更改的地方,请随时提及。组预处理非常快,并且通过控制每个 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;
}
4

1 回答 1

0

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

http://www.ormbattle.net/

现在,要回答您的问题,要在 EF 中进行批量更新,您需要下载一些能够启用此类功能的扩展和第三方插件。请参阅:批量更新/删除 EF5

于 2013-06-06T21:32:34.653 回答