Как избежать блокировки базы данных в Entity Framework 4 при выполнении многих обновлений - PullRequest
13 голосов
/ 16 марта 2011

Этот вопрос касается наилучшей практики обработки множества вставок или обновлений с использованием Microsoft Entity Framework. Проблема в том, что мы написали долгосрочную программу, которая извлекает тысячи записей из базы данных, а затем обновляет по одному полю для каждой из этих записей, одну за другой. К нашему большому разочарованию, мы поняли, что каждая из этих обновленных записей была заблокирована на время, в течение которого ObjectContext не был удален. Ниже приведен псевдокод (на самом деле не запускается) для иллюстрации:

using(ObjectContext context = new ObjectContext())
{

    var myRecords = context.CreateObjectSet<MyType>().AsQueryable();

    foreach(var record in myRecords)
    {
       record.MyField = "updated!";
       context.SaveChanges();

       //--do something really slow like call an external web service
   }
}

Проблема в том, что нам нужно много обновлений без учета транзакций. Мы были удивлены, осознав, что вызов context.SaveChanges () фактически создает блокировку записей и не снимает ее, пока не будет удален ObjectContext. Мы особенно НЕ хотим блокировать записи в базе данных, так как это система с большим трафиком, и программа может потенциально работать часами.

Таким образом, вопрос заключается в следующем: каков оптимальный способ сделать много обновлений в Microsoft Entity Framework 4 БЕЗ выполнения их всех в одной длинной транзакции, которая блокирует БД? Мы надеемся, что ответом будет не создание нового ObjectContext для каждого обновления ...

Ответы [ 4 ]

7 голосов
/ 16 марта 2011

Платформа сущностей поверх SQL-сервера по умолчанию использует уровень изоляции транзакции чтения, а транзакция фиксируется в конце SaveChanges. Если вы подозреваете другое поведение, это должно происходить в остальной части вашего кода (вы используете TransactionScope?

Также ваш подход неверен. Если вы хотите сохранить каждую запись отдельно, вы должны также загрузить каждую запись отдельно. EF определенно плохой выбор для этого типа приложений Даже если вы используете только один SaveChange для обновления всех своих записей, он все равно будет совершать одну поездку в базу данных для каждого обновления.

4 голосов
/ 16 марта 2011

Эти блокировки не создаются Entity Framework.EF поддерживает только оптимистичный параллелизм, пессимистическая блокировка не поддерживается с EF.

Я думаю, что блокировка, которую вы испытываете, является результатом вашей конфигурации SQL Server.Возможно, если для вашего уровня изоляции транзакций на сервере установлено значение REPEATABLE READ, это может вызвать блокировки после каждого запроса.Но я не уверен, какая именно конфигурация может быть проблемой.Подробнее здесь .

Редактировать:

Другая полезная статья о транзакциях и изоляции транзакций в EF - здесь .Настоятельно рекомендуется всегда устанавливать уровень изоляции явно.Цитата из статьи:

Если вы не берете под контроль [уровень изоляции], вы не представляете, на каком уровне изоляции транзакций будут выполняться ваши запросы .В конце концов, вы не знаете, где было соединение, которое вы получили из пула [...]. Вы просто наследуете последний использованный уровень изоляции в соединении, поэтому вы не знаете, какой тип блокировки взят (или хуже).: игнорируется) вашими запросами и как долго эти блокировки будут удерживаться.В загруженной базе данных это определенно приведет к случайным ошибкам, тайм-аутам и тупикам .

2 голосов
/ 16 марта 2011

Возможно, я ошибаюсь, но я считаю, что вам не следует вызывать SaveChanges () каждый раз, так как это применяет изменения к базе данных на этом этапе. Вместо этого примените SaveChanges () в конце изменения объекта или используйте счетчик, чтобы делать это реже.

1 голос
/ 19 августа 2016

В нашем приложении у нас был похожий сценарий, избегайте максимально возможной блокировки при выполнении массивного выбора, а затем создавайте много вставок после некоторой операции с памятью.

  1. Если вы хотите прочитать все заранее

Решение A ) Используйте область транзакции, которая включает чтение и обновление PRO: данные безопасно обновлены Минусы: блокировки вызваны чтением (повторяющиеся чтения) и обновлением

Решение B ) Не используйте транзакцию и обновляйте все данные вместе PRO: данные безопасно обновлены, но данные, которые вы прочитали, могли быть изменены CONS: блокировки, вызванные обновлением на весь период (EF создает по умолчанию транзакцию)

Решение C ) Обновлять пакетами вместо всех данных все вместе (можно использовать, только если выбор не блокирует таблицы, в противном случае вы получаете то же поведение, что и B PRO: короткие и меньшие блокировки в обновленных таблицах Минусы: Вы увеличиваете изменение подверженности устареванию данных

  1. Если вы хотите (и можете) читать партиями

Решение D ) Разбивка проблемы и разбиение операций чтения могут помочь вам уменьшить блокировку, чтобы вы могли использовать область транзакции для переноса как чтения, так и записи (как решение A) PRO: данные безопасно обновлены Минусы: блокировки, вызванные чтением (повторяющиеся чтения) и обновлением, воздействия варьируются в зависимости от размера пакета и характера самого запроса

Решение E ) Не используйте транзакции, поэтому только обновление вызовет небольшие блокировки (как в решении B) PRO: данные безопасно обновлены, но данные, которые вы прочитали, могли быть изменены Минусы: блокировки вызваны обновлениями

Как правильно заметил @Ladislav, множественные вставки действительно неэффективны, и быстрое профилирование базы данных показывает, как в этом случае происходит сбой волшебства ORM. Если вы хотите использовать EF для выполнения пакетных операций, таких как вставки, обновления и удаления, я рекомендую вам взглянуть на это: EF Utilities

Я склонен проверять блокировки с помощью этого запроса, надеюсь, поможет лучше понять, что происходит.

SELECT
    OBJECT_NAME(p.OBJECT_ID) AS TableName,
    resource_type,
    resource_description
FROM
    sys.dm_tran_locks l JOIN 
    sys.partitions p ON 
    l.resource_associated_entity_id = p.hobt_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...