Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在.NET中优化代码性能_C#_Multithreading_Entity Framework_Optimization - Fatal编程技术网

C# 如何在.NET中优化代码性能

C# 如何在.NET中优化代码性能,c#,multithreading,entity-framework,optimization,C#,Multithreading,Entity Framework,Optimization,我有一个导出作业,将数据从旧数据库迁移到新数据库。我面临的问题是,用户数量约为300万,完成这项工作需要很长时间(15个多小时)。我正在使用的机器只有一个处理器,所以我不确定线程化是否是我应该做的。有人能帮我优化这个代码吗 static void ExportFromLegacy() { var usersQuery = _oldDb.users.Where(x => x.status == 'active'); int BatchSize = 1000;

我有一个导出作业,将数据从旧数据库迁移到新数据库。我面临的问题是,用户数量约为300万,完成这项工作需要很长时间(15个多小时)。我正在使用的机器只有一个处理器,所以我不确定
线程化
是否是我应该做的。有人能帮我优化这个代码吗

static void ExportFromLegacy()
{
    var usersQuery = _oldDb.users.Where(x =>
        x.status == 'active');

    int BatchSize = 1000;
    var errorCount = 0;
    var successCount = 0;
    var batchCount = 0;

    // Using MoreLinq's Batch for sequences
    // https://www.nuget.org/packages/MoreLinq.Source.MoreEnumerable.Batch
    foreach (IEnumerable<users> batch in usersQuery.Batch(BatchSize))
    {
        Console.WriteLine(String.Format("Batch count at {0}", batchCount));
        batchCount++;

        foreach(var user in batch)
        {
            try
            {               
                var userData = _oldDb.userData.Where(x =>
                    x.user_id == user.user_id).ToList();

                if (userData.Count > 0)
                {                   
                    // Insert into table
                    var newData = new newData()
                    {                       
                        UserId = user.user_id; // shortened code for brevity.                       
                    };

                    _db.newUserData.Add(newData);
                    _db.SaveChanges();

                    // Insert item(s) into table
                    foreach (var item in userData.items)
                    {
                        if (!_db.userDataItems.Any(x => x.id == item.id)
                        {
                            var item = new Item()
                            {                               
                                UserId = user.user_id, // shortened code for brevity.   
                                DataId = newData.id // id from object created above
                            };

                            _db.userDataItems.Add(item);                            
                        }

                        _db.SaveChanges();
                        successCount++;
                    }
                }               
            }
            catch(Exception ex)
            {
                errorCount++;
                Console.WriteLine(String.Format("Error saving changes for user_id: {0} at {1}.", user.user_id.ToString(), DateTime.Now));
                Console.WriteLine("Message: " + ex.Message);
                Console.WriteLine("InnerException: " + ex.InnerException);
            }
        }
    }

    Console.WriteLine(String.Format("End at {0}...", DateTime.Now));
    Console.WriteLine(String.Format("Successful imports: {0} | Errors: {1}", successCount, errorCount));
    Console.WriteLine(String.Format("Total running time: {0}", (exportStart - DateTime.Now).ToString(@"hh\:mm\:ss")));
}
静态void exportfromleagacy()
{
var usersQuery=\u oldDb.users.Where(x=>
x、 状态==“活动”);
int BatchSize=1000;
var errorCount=0;
var successCount=0;
var-batchCount=0;
//使用MoreLinq批处理序列
// https://www.nuget.org/packages/MoreLinq.Source.MoreEnumerable.Batch
foreach(usersqery.batch(BatchSize)中的IEnumerable批处理)
{
WriteLine(String.Format(“在{0}处的批计数”,batchCount));
batchCount++;
foreach(批处理中的var用户)
{
尝试
{               
var userData=\u oldDb.userData.Where(x=>
x、 user\u id==user.user\u id).ToList();
如果(userData.Count>0)
{                   
//插入表格
var newData=newnewdata()
{                       
UserId=user.user\u id;//为简洁起见,缩短了代码。
};
_db.newUserData.Add(newData);
_db.SaveChanges();
//将项目插入表中
foreach(userData.items中的var项)
{
if(!\u db.userDataItems.Any(x=>x.id==item.id)
{
变量项=新项()
{                               
UserId=user.user\u id,//为简洁起见,缩短了代码。
DataId=newData.id//id来自上面创建的对象
};
_db.userDataItems.Add(item);
}
_db.SaveChanges();
successCount++;
}
}               
}
捕获(例外情况除外)
{
errorCount++;
WriteLine(String.Format(“在{1}保存用户{0}的更改时出错)”,user.user.id.ToString(),DateTime.Now);
Console.WriteLine(“消息:+ex.Message”);
Console.WriteLine(“InnerException:+ex.InnerException”);
}
}
}
WriteLine(String.Format(“结束于{0}…”,DateTime.Now));
WriteLine(String.Format(“成功导入:{0}|错误:{1}”,成功计数,错误计数));
WriteLine(String.Format(“总运行时间:{0}”,(exportStart-DateTime.Now).ToString(@“hh\:mm\:ss”));
}

实体框架是导入大量数据的一个非常糟糕的选择。我从个人经验中知道这一点

也就是说,当我尝试以与您相同的方式使用它时,我发现了一些优化方法

Context
将在添加对象时缓存对象,插入次数越多,以后的插入速度就越慢。我的解决方案是在处理该实例并创建新实例之前,将每个上下文限制在500次左右。这大大提高了性能

我可以使用多个线程来提高性能,但您必须非常小心资源争用。每个线程都肯定需要自己的
上下文
,甚至不要考虑尝试在线程之间共享它。我的机器有8个内核,因此线程可能对您没有多大帮助;只有一个内核我怀疑这对你有什么帮助

使用
AutoDetectChangesEnabled=false;
关闭更改跟踪,更改跟踪速度非常慢。不幸的是,这意味着您必须修改代码,以便直接通过上下文进行所有更改。不再使用
Entity.Property=“Some Value”
,它将变成
context.Entity(e=>e.Property).SetValue(“Some Value”);
(或者类似的东西,我不记得确切的语法),这使得代码很难看

任何查询都应该使用
AsNoTracking

尽管如此,我还是能够将大约20小时的过程缩短到6小时左右,但我仍然不推荐使用EF。这是一个非常痛苦的项目,因为我几乎完全没有选择EF来添加数据。请使用其他东西…任何其他东西


我不想给人留下这样的印象,EF是一个糟糕的数据访问库,它在设计上非常出色,不幸的是,这不是它的设计目的。

实体框架对于导入大量数据来说是一个非常糟糕的选择。我从个人经验中知道这一点

也就是说,当我尝试以与您相同的方式使用它时,我发现了一些优化方法

Context
将在添加对象时缓存对象,插入次数越多,以后的插入速度就越慢。我的解决方案是在处理该实例并创建新实例之前,将每个上下文限制在500次左右。这大大提高了性能

我可以使用多个线程来提高性能,但您必须非常小心资源争用。每个线程都肯定需要自己的
上下文
,甚至不要考虑尝试在线程之间共享它。我的机器有8个内核,因此线程可能对您没有多大帮助;只有一个内核我怀疑这对你有什么帮助

使用
AutoDetectChangesEnabled=false;
关闭变更跟踪,变更跟踪速度非常慢。不幸的是,这意味着您需要
foreach (...){
}
successCount += _db.SaveChanges();
List<ObjClass> list = new List<ObjClass>();
foreach (...)
{
  list.Add(new ObjClass() { ... });
}
_db.newUserData.AddRange(list);
successCount += _db.SaveChanges();
List<ObjClass> list = new List<ObjClass>();
int cnt=0;
foreach (...)
{
  list.Add(new ObjClass() { ... });
  if (++cnt % 100 == 0) // bunches of 100
  {
    _db.newUserData.AddRange(list);
    successCount += _db.SaveChanges();
    list.Clear();
    // Optional if a HUGE amount of data
    if (cnt % 1000 == 0)
    {
      _db = new MyDbContext();
    } 
  }
}
// Don't forget that!
_db.newUserData.AddRange(list);
successCount += _db.SaveChanges();
list.Clear();
var list = batch.Select(x => x.user_id).ToList();
var userDatas = _oldDb.userData
                  .AsNoTracking()
                  .Where(x => list.Contains(x.user_id))
                  .ToList();

foreach(var userData in userDatas)
{
    ....
}
foreach (IEnumerable<users> batch in usersQuery.Batch(BatchSize))
{
    // Retrieve all users for the batch at once.
   var list = batch.Select(x => x.user_id).ToList();
   var userDatas = _oldDb.userData
                         .AsNoTracking()
                         .Where(x => list.Contains(x.user_id))
                         .ToList(); 

    // Create list used for BulkInsert      
    var newDatas = new List<newData>();
    var newDataItems = new List<Item();

    foreach(var userData in userDatas)
    {
        // newDatas.Add(newData);

        // newDataItem.OwnerData = newData;
        // newDataItems.Add(newDataItem);
    }

    _db.BulkInsert(newDatas);
    _db.BulkInsert(newDataItems);
}
public class UserData
{
    public int UserDataID { get; set; }
    // ... properties ...

    public List<UserDataItem> Items { get; set; }
}

public class UserDataItem
{
    public int UserDataItemID { get; set; }
    // ... properties ...

    public UserData OwnerData { get; set; }
}

var userData = new UserData();
var userDataItem = new UserDataItem();

// Use navigation property to set the parent.
userDataItem.OwnerData = userData;
foreach (IEnumerable<users> batch in usersQuery.Batch(BatchSize))
{
    // Retrieve all users for the batch at once.
   var list = batch.Select(x => x.user_id).ToList();
   var userDatas = _oldDb.userData
                         .AsNoTracking()
                         .Where(x => list.Contains(x.user_id))
                         .ToList(); 

    // Create list used for BulkInsert      
    var newDatas = new List<newData>();
    var newDataItems = new List<Item();

    foreach(var userData in userDatas)
    {
        // newDatas.Add(newData);

        // newDataItem.OwnerData = newData;
        // newDataItems.Add(newDataItem);
    }

    var context = new UserContext();
    context.userDatas.AddRange(newDatas);
    context.userDataItems.AddRange(newDataItems);
    context.BulkSaveChanges();
}