Проблема производительности платформы Entity, saveChanges очень медленная
В последнее время я занимаюсь простой работой EF. Очень просто,
во-первых,
List<Book> books = entity.Books.WHERE(c=>c.processed==false)ToList();
then
foreach(var book in books)
{
//DoSomelogic, update some properties,
book.ISBN = "ISBN " + randomNumber();
book.processed = true;
entity.saveChanges(book)
}
Я помещаю entity.saveChanges
в foreach
, потому что это большой список, около 100 тыс. записей, и если эта запись обрабатывается без проблем, то отметьте эту запись, установите book.processed = true, если процесс прерван по исключению, то в следующий раз мне больше не нужно обрабатывать эти хорошие записи.
Все кажется мне хорошо. Это быстро, когда вы обрабатываете сотни записей. Затем, когда мы переходим на 100k записей, entity.saveChanges очень медленный. около 1-3 секунд на запись. Затем мы сохраняем модель сущности, но заменим entity.saveChanges
на классический SqlHelper.ExecuteNonQuery("update_book", sqlparams)
. И это очень быстро.
Может ли кто-нибудь сказать мне, почему процесс структуры сущностей медленный? И если я все еще хочу использовать entity.saveChanges, что лучший способ повысить производительность?
Спасибо
Ответы
Ответ 1
Отключите отслеживание изменений, прежде чем выполнять свои вставки. Это значительно улучшит вашу производительность (величины порядка). Помещение SaveChanges()
за пределы вашего цикла также поможет, но отключение отслеживания изменений поможет еще больше.
using (var context = new CustomerContext())
{
context.Configuration.AutoDetectChangesEnabled = false;
// A loop to add all your new entities
context.SaveChanges();
}
Для получения дополнительной информации см. эту страницу.
Ответ 2
Я бы взял SaveChanges (книгу) за пределами foreach. Поскольку книга относится к объекту в виде списка, вы можете поместить его снаружи, и EF будет работать лучше с полученным кодом.
Список - это атрибут объекта, а EF предназначен для оптимизации обновлений/создания/удаления в базе данных. Если вы сделаете это, мне будет интересно, поможет ли это.
Ответ 3
Я также могу посоветовать вам вывести SaveChanges() из цикла, так как он "n" числа обновлений в базе данных, поэтому в контексте будет "n" раз, чтобы итерации через контрольные точки и проверки.
var books = entity.Books.Where(c => c.processed == false).ToList();
books.Foreach(b =>
{
b.ISBN = "ISBN " + randomNumber();
b.processed = true;
//DoSomelogic, update some properties
});
entity.SaveChanges();
Ответ 4
" AsNoTracking" работает для меня
ex:
Item itemctx = ctx.Items.AsNoTracking().Single(i=>i.idItem == item.idItem);
ctx.Entry(itemctx).CurrentValues.SetValues(item);
itemctx.images = item.images;
ctx.SaveChanges();
Без "AsNoTracking" обновления происходят очень медленно.
Ответ 5
Entity Framework, на мой взгляд, является плохим выбором для операций BULK как с точки зрения производительности, так и с точки зрения потребления памяти. Как только вы выйдете за пределы нескольких тысяч записей, метод SaveChanges действительно начинает разрушаться.
Вы можете попытаться разделить свою работу на более мелкие транзакции, но опять же, я думаю, вы слишком много работаете, чтобы создать это.
Гораздо лучший подход заключается в том, чтобы использовать операции BULK, которые уже предоставлены вашей СУБД. SQL Server предоставляет BULK COPY через .NET. Oracle предоставляет BULK COPY для доступа к Oracle.DataAccess или неуправляемого доступа к данным. Для Oracle.ManagedDataAccess библиотека BULK COPY, к сожалению, недоступна. Но я могу создать хранимую процедуру Oracle, используя BULK COLLECT/FOR ALL, которая позволяет мне вставлять тысячи записей за считанные секунды с гораздо меньшим объемом памяти в вашем приложении. В приложении .NET вы можете реализовать ассоциативные массивы PLSQL в качестве параметров и т.д.
Преимущество использования средств BULK в вашей СУБД сводится к сокращению контекстных переключений между вашим приложением, процессором запросов и механизмом базы данных.
Я уверен, что другие поставщики баз данных предоставляют что-то подобное.
Ответ 6
Используйте этот пакет Nuget:
Z.EntityFramework.Extensions
У него есть методы расширения, которые вы можете вызывать для DbContext, например DbContext.BulkSaveChanges, который работает удивительно быстро.