C # выполняет массовое удаление на 50000 и более записей - PullRequest
0 голосов
/ 03 октября 2018

У меня есть проблема, когда у меня есть фрагмент кода, подобный следующему:

  var inPast = DateTime.Today.AddDays(-30);
    DBRetry.Do(() => EFBatchOperation.For(ctx, ctx.Transactions).Where(t =>  t.TransactionDate <= inPast).Delete(), TimeSpan.FromSeconds(2));

Функция DBRetry просто повторяет операцию каждые 2 секунды, если происходит тайм-аут или тупик ...

Теперь проблема в том, что моя таблица Transactions содержит более 100 миллионов записей ...

Как вы видите, я пытаюсь удалить все записи старше 30 дней ... Однако этоне работает, так как я постоянно получаю тайм-аут, как показано ниже:

Истекло время ожидания выполнения.Период ожидания истек до завершения операции, или сервер не отвечает.

Столбец TransactionDate проиндексирован и является неуникальным и не кластеризованным индексом, но это неКажется, это помогает ... Библиотека, которую я использую для массового удаления, выглядит следующим образом:

https://github.com/MikaelEliasson/EntityFramework.Utilities

Кто-нибудь знает более эффективное решение или как мне решить эту проблему?

Ответы [ 5 ]

0 голосов
/ 22 января 2019
DbContext.Database.CommandTimeout = 0; // set unlimited timeout

Или установите его в 0 в файле конфигурации

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.commandtimeout?view=netframework-4.7.2

0 голосов
/ 04 октября 2018

Поскольку удаление 50К-строк может занять более 2 секунд, делайте это гораздо меньшими порциями;скажем 1000.

Кроме того, не "делайте это каждые 2 секунды";вместо этого «делай это постоянно».То есть, закончив с одной партией, сделайте следующую партию.При желании можно сделать короткую паузу между партиями.Выполнение удаления по фиксированному расписанию может привести к путанице над несколькими копиями, работающими одновременно.

Здесь рассматриваются несколько методов: http://mysql.rjweb.org/doc.php/deletebig

Обратите внимание, что эта ссылка предлагает PARTITION BY RANGE(TO_DAYS()) как оптимальный способделать большие удаления.Ежедневные (или еженедельные) разделы будут рекомендованы.

0 голосов
/ 03 октября 2018

Я бы предпринял некоторые действия, чтобы смягчить эту проблему;

1) В Entity Framework, как правило, существует требование, чтобы записи / объекты загружались в память, прежде чем вы сможете их удалить, что может быть большимпроизводительность поразила сама по себе.

Может быть лучше вместо этого запустить несколько пользовательских SQL для этой операции

2) Создать индексы в таблице базы данных для даты транзакции поля

Представьте себе, что база данных должнаВыполните запрос в большой таблице, чтобы выполнить запрос select-> where, учитывая достаточно большой набор записей, поэтому он должен просмотреть все их, чтобы определить, какие записи вам нужны.Добавление индексов к этой таблице помогает определить в базе данных, какие поля вы запрашиваете чаще всего, и позволяет базе данных оптимизировать эти операции для вас.

3) Выполнять запрос чаще, чем за 30 дней

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

4) Удалять записи партиями

Если для этого вам нужно использовать Entity Framework, выможете выбрать строки, которые вы хотите удалить партиями по X раз;эта справка распределяет нагрузку на базу данных, на случай, если операция может занять много минут.

0 голосов
/ 03 октября 2018

Я не думаю, что индексы являются решением и могут быть проблемой.Если таблица имеет индексы в нескольких столбцах, удаление может блокировать записи для обновления индексов.Это дорого и требует времени, пытаясь разбить его.Получите список идентификаторов для записей, а затем удалите их, скажем, 10 000

0 голосов
/ 03 октября 2018

Вы можете попробовать увеличить время ожидания команды для вашего контекста БД:

(Первоначально на MSDN )

public class YourContext : DbContext
{
  public YourContext()
    : base("YourConnectionString")
 {
    // Get the ObjectContext related to this DbContext
    var objectContext = (this as IObjectContextAdapter).ObjectContext;

    // Sets the command timeout for all the commands
    objectContext.CommandTimeout = 120;
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...