【问题标题】:Entity Framework bulk insert unreal slow实体框架批量插入虚幻慢
【发布时间】:2016-03-27 09:16:51
【问题描述】:

我正在使用 EF 6。我尝试插入大约 200.000 个实体,同时在每 100 个实体之后保存对数据库的更改。

问题是保存 50.000 个实体需要 11 个小时,但仍然落后。我正在使用 WebJobs 运行它,并且作业发布在与主网站相同的 azure webapp 上。是因为这个问题和 WebJob 没有足够的资源,还是在 100 个实体后保存,还是方法?

方法

public void SaveLeadsForBuyer(ISenderModel model)
{
    var rowCounter = 0;

    foreach (var deliveryRecord in model.Customers.Select(customerModel => new DeliveryRecord()
    {
        BuyerId = model.Buyer.Id,
        AspNetUserId = customerModel.Id,
        DeliveryType = model.Buyer.DeliveryType,
        CreatedOn = DateTime.UtcNow
    }))
    {
        ++rowCounter;

        _unit.Repository<DeliveryRecord>().Insert(deliveryRecord);

        _unit.SaveChangesPartially(rowCounter, 100);
    }

    _unit.SaveChanges();
}

助手

public static class UnitOfWorkHelper
{
    /// <summary>
    /// Helper method triggers SaveChanges() after amount of rows provided through "amount" parameter in method
    /// </summary>
    /// <param name="unit">UnitOfWork object</param>
    /// <param name="count">Current amount of rows</param>
    /// <param name="saveCount">Amount when to save changes to database</param>
    public static void SaveChangesPartially(this IUnitOfWorkAsync unit, int count, int saveCount)
    {
        if (count % saveCount == 0)
        {
            unit.SaveChanges();
        }
    }
}

【问题讨论】:

  • EF 对批量插入非常不利,INSERT SELECT 的运行速度是 EF 的 1000000 倍。
  • 不是您正在做的“批量插入” - 您需要查看 EntityFramework.BulkInsert 或其他组件(搜索 在你最喜欢的“实体框架批量插入”搜索引擎上 - 你会找到几个,选择你最喜欢的一个)

标签: c# entity-framework azure azure-webjobs


【解决方案1】:

这很慢,因为 Entity Framework 为每条记录执行数据库往返。因此,如果您保存 200,000 个实体,则将执行 200,000 次数据库往返,这对于保存多个实体而言是最佳选择。

对于这种场景,您需要自己实现或使用支持 BulkInsert 的库(通常在后台执行 SqlBulkCopy)

有 3 个允许批量插入的主库(2 个免费,1 个专业版)

// Example from Entity Framework Extensions Library
using (var ctx = new EntitiesContext())
{
    ctx.BulkInsert(list);
}

您可以阅读以下文章以了解每个库的优缺点:Entity Framework - Bulk Insert Library Reviews & Comparisons

Entity Framework Extensions 是迄今为止提供最大灵活性(批量插入、更新、删除、合并和批量保存更改并支持一切)的库,但它是专业版。如果您正在寻找免费版本,我建议使用 EntityFramework.BulkInsert,但是,它不再受支持,并且不支持所有关联和继承。

免责声明:我是项目的所有者Entity Framework Extensions

编辑:回答评论问题

我保存的是每 100 条记录,而不是每条记录

无论您向单元上下文添加一个实体还是 100 个实体都没有关系,Entity Framework 会一个一个地保存它们(每条记录的单个插入语句)。只需将 SQL Profiler 与 SQL Server 数据库一起使用,您就会明白我的意思。

编辑:回答评论问题

伟大的乔纳森。有什么方法可以用 ef6 generic 实现这个 哇?

答案取决于您选择使用哪个库。

如果您使用我的库,您可以创建 BulkSaveChanges 方法或在您的 UnitOfWork 中将所有“_context.SaveChanges()”更改为“_context.BulkSaveChanges()”

public void SaveLeadsForBuyer(ISenderModel model)
{
    // ... code ...
    // _unit.SaveChanges();
    _unit.BulkSaveChanges();
}

如果您想要最好的性能并从我的库或免费库中实现批量插入,我可能会添加一个名为 BulkInsert 的方法或扩展方法(如果您无法更改存储库类)

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    // ... code ...

    public virtual void BulkInsert(List<TEntity> list)
    {
        _context.BulkInsert(list);
    }
}

请记住 BulkInsert 直接插入实体而无需调用“SaveChanges”,它不使用上下文/更改跟踪器来获得最佳性能。

【讨论】:

  • 我保存的是每 100 条记录,而不是每条记录。
  • 没关系。你使用了不适合这项工作的工具。 EF 不适用于批量转账。正如乔纳森所说 - 也就是说,SqlBulkCopy 是由应该被解雇的人编写的(非常糟糕的锁定行为)......我在半天之内编写了自己的类,创建了一个临时表,将 sql 批量复制到其中,然后使用一个 sql 命令将数据复制到最终表。避免了 SqlBulkCopy 中荒谬的锁定逻辑(它试图获得 30 秒的排他锁,在循环中不等待,所以如果表上有任何活动,它永远不会得到它)。
  • 伟大的乔纳森。有没有办法用 ef6 通用 uow 来实现这个?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-07
  • 2020-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多