EF ObjectContext中的批量插入性能问题

时间:2013-09-16 07:10:03

标签: mysql performance entity-framework bulkinsert

我正在尝试将大量行(> 10,000,000)插入MySQL 使用EF ObjectContext(db-first)的数据库。看完this question的答案后 我写了这个代码(批量保存)来插入大约10,000个联系人(实际上是30k行;包括相关的其他行):

// var myContactGroupId = ...;

const int maxContactToAddInOneBatch = 100;
var numberOfContactsAdded = 0;          

// IEnumerable<ContactDTO> contacts = ...

foreach (var contact in contacts)
{
    var newContact = AddSingleContact(contact); // method excerpt below

    if (newContact == null)
    {
        return;
    }

    if (++numberOfContactsAdded % maxContactToAddInOneBatch == 0)
    {
        LogAction(Action.ContactCreated, "Batch #" + numberOfContactsAdded / maxContactToAddInOneBatch);
        _context.SaveChanges();
        _context.Dispose();
        // _context = new ...
    }
}

// ...

private Contact AddSingleContact(ContactDTO contact)
{
    Validate(contact); // Simple input validations  

    // ...
    // ...

    var newContact = Contact.New(contact); // Creates a Contact entity

    // Add cell numbers
    foreach (var cellNumber in contact.CellNumbers)
    {
        var existingContactCell = _context.ContactCells.FirstOrDefault(c => c.CellNo == cellNumber);

        if (existingContactCell != null)
        {
            // Set some error message and return
            return;
        }

        newContact.ContactCells.Add(new ContactCell
        {
            CellNo = cellNumber,
        });
    }

    _context.Contacts.Add(newContact);

    _context.ContactsInGroups.Add(new ContactsInGroup
    {
        Contact = newContact,
        // GroupId =  some group id
    });

    return newContact;
}

但似乎添加的联系人越多(批量),需要更多时间(非线性)。 这是批量大小100(10k联系人)的日志。注意随着批次#增加所需的时间增加:

12:16:48    Batch #1
12:16:49    Batch #2
12:16:49    Batch #3
12:16:50    Batch #4
12:16:50    Batch #5
12:16:50    Batch #6
12:16:51    Batch #7
12:16:52    Batch #8
12:16:53    Batch #9
12:16:54    Batch #10

...
...

12:21:26    Batch #89
12:21:32    Batch #90
12:21:38    Batch #91
12:21:44    Batch #92
12:21:50    Batch #93
12:21:57    Batch #94
12:22:03    Batch #95
12:22:10    Batch #96
12:22:16    Batch #97
12:22:23    Batch #98
12:22:29    Batch #99
12:22:36    Batch #100

花了6分48秒。如果我将批量大小增加到10,000(需要一个批次),则需要大约26秒(对于10k接触)。但是,当我尝试插入100k触点(每批10k)时,需要很长时间(因为我猜每批次的时间增加)。

你能解释一下为什么尽管上下文正在更新,但它正在花费越来越多的时间吗? 除了原始SQL之外还有其他的想法吗?

2 个答案:

答案 0 :(得分:0)

关于您使用context.Configuration.AutoDetectChangesEnabled = false;链接的问题的大多数答案我在您的示例中没有看到。所以你应该试试。您可能也想考虑EF6。为此目的,它在上下文中有一个AddRange方法,请参阅INSERTing many rows with Entity Framework 6 beta 1

答案 1 :(得分:0)

终于明白了。看起来方法Validate()是罪魁祸首。它有一个存在检查查询来检查联系人是否已经存在。因此,随着联系人的添加,数据库会增长,并且随着批次#的增加需要更多的时间来检查;主要是因为细胞编号字段(它正在比较)没有编入索引。