WedX - журнал о программировании и компьютерных науках

Как ускорить удаление из большой таблицы базы данных?

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

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

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

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

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

Любые идеи? Я уже читал здесь более старые посты по теме и не нашел ничего, что могло бы помочь.


  • Почему нельзя временно отключить внешние ключи? Можете ли вы не запустить это во время периода обслуживания и отключить доступ к системе? 21.07.2009
  • Можно ли вообще избавиться от необходимости выполнять этот процесс копирования? Обеспечивая с помощью некоторого алгоритма, что записи делятся поровну между осколками с самого начала, вместо того, чтобы перемещать записи после того, как они были созданы, чтобы сбалансировать их. 21.07.2009
  • @AdaTheDev, для новых шардов это не такая уж проблема, но мое первоначальное развертывание было резервным копированием и восстановлением, и на каждой копии помечалась половина данных как фактически удаленная. Итак, эти два осколка огромны. @Mitch, смысл осколков в том, чтобы использовать более дешевое оборудование, поэтому я не буду тратить деньги на решение проблемы. @ck, я не уверен, что настоящая проблема - это FK. Я думаю, что больше всего времени тратится на удаление кластерного индекса. 21.07.2009
  • Вы можете разместить здесь план выполнения? Чтобы получить его, нажмите Control-L, когда ваш запрос загружен в SSMS, а затем сделайте снимок экрана (при условии, что все это умещается на одном экране). Если он слишком велик, посмотрите, сможете ли вы получить текстовую копию плана. 24.07.2009
  • Я не хочу публиковать фактический план, но могу описать его достаточно легко: удаление кластерного индекса в крайнем правом углу с 66%, затем под этими 29 поисками FK примерно по 1% каждый. 25.07.2009

Ответы:


1

См. Раздел Оптимизация удаления на SQL Server

Эта статья поддержки MS может быть интересна: Как решить проблемы с блокировкой, вызванные эскалацией блокировок в SQL Server :

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

DELETE FROM LogMessages WHERE LogDate < '2/1/2002'    

Удаляя эти записи по несколько сотен за раз, вы можете значительно уменьшить количество блокировок, накапливаемых за транзакцию, и предотвратить эскалацию блокировок. Например:

SET ROWCOUNT 500
delete_more:
     DELETE FROM LogMessages WHERE LogDate < '2/1/2002'
IF @@ROWCOUNT > 0 GOTO delete_more
SET ROWCOUNT 0

Уменьшите объем блокировки запроса, сделав запрос как можно более эффективным. Большое сканирование или большое количество поисков по закладкам могут увеличить вероятность эскалации блокировки; кроме того, это увеличивает вероятность возникновения взаимоблокировок и, как правило, отрицательно сказывается на параллелизме и производительности.

21.07.2009
  • Это именно то, что я собирался предложить. 21.07.2009
  • @crokusek, добавлю к вашему комментарию, это не повлияет на эту конкретную партию, поскольку SET ROWCOUNT 500 делает ее 500 для этой партии. Любые последующие партии могут быть затронуты, если они ожидают, что ROWCOUNT (отличный от @@ROWCOUNT) будет чем-то другим. 10.02.2015
  • Это просто потрясающе. Никогда не думал смотреть на проблему так. Все, что я могу сказать, это вау! 01.03.2017

  • 2
    delete_more:
         DELETE TOP(500) FROM LogMessages WHERE LogDate < '2/1/2002'
    IF @@ROWCOUNT > 0 GOTO delete_more
    

    Вы можете достичь того же результата, используя SET ROWCOUNT, как предлагает Митч, но согласно MSDN он не будет поддерживаться для DELETE и некоторых других операций в будущих версиях SQL Server:

    Использование SET ROWCOUNT не повлияет на операторы DELETE, INSERT и UPDATE в будущих выпусках SQL Server. Избегайте использования SET ROWCOUNT с операторами DELETE, INSERT и UPDATE в новых разработках и запланируйте изменение приложений, которые в настоящее время его используют. Для аналогичного поведения используйте синтаксис TOP. Дополнительные сведения см. В разделе TOP (Transact-SQL).

    31.07.2013
  • А RowCount несет в себе последствия для области видимости stackoverflow.com/questions / 5383761 /. Например, как вы узнаете, что он был нулевым с самого начала, например, когда он будет восстановлен? 19.11.2013

  • 3

    Вы можете создать новые файлы, скопировать все, кроме «удаленных» строк, а затем поменять местами имена в таблицах. Наконец, отбросьте старые таблицы. Если вы удаляете большой процент записей, это может быть быстрее.

    21.07.2009
  • Я мог бы попробовать это, но мы говорим о таблицах с десятками миллионов записей, несколькими гигабайтами для кластерного индекса. Это должно быть возможно в рамках обычного окна обслуживания. 21.07.2009

  • 4

    Другое предложение - переименовать таблицу и добавить столбец статуса. Когда status = 1 (удален), вы не хотите, чтобы он отображался. Таким образом, вы затем создаете представление с тем же именем, что и исходная таблица, которая выбирает из таблицы, когда статус равен нулю или = 0 (в зависимости от того, как вы его реализуете). Удаление отображается немедленно для пользователя, и каждые пятнадцать минут может выполняться фоновое задание, удаляющее записи, которое выполняется без ведома никого, кроме dbas.

    31.07.2013

    5

    Если вы используете SQL 2005 или 2008, возможно, вам поможет «изоляция моментальных снимков». Он позволяет данным оставаться видимыми для пользователей во время обработки базовой операции обновления данных, а затем показывает данные сразу после их фиксации. Даже если удаление занимает 30 минут, ваши приложения все это время останутся в сети.

    Вот краткое руководство по блокировке снимков:

    https://www.mssqltips.com/tip.asp?tip=1081

    Хотя вам все равно следует попытаться ускорить удаление, чтобы оно происходило как можно быстрее, это может частично облегчить бремя.

    24.07.2009
  • Проблема в том, что я не говорю о 30 минутах. Я говорю о десятках миллионов строк, удаление которых занимает у меня более 1 секунды. Это в сумме составляет несколько месяцев. 24.07.2009
  • 1 секунда на строку для удаления - это слишком много. Какое железо? 27.03.2010
  • @TomTom: Хотя это очень медленно, но это вполне понятно. Возможно, оборудование работает медленно или с высокой конкуренцией, или, может быть, удаления являются каскадными, или в таблице, которую вы удаляете, есть кластерный индекс, который вызывает большое физическое перемещение данных, или, возможно, некоторые триггеры удаления, которые заняты выполнением каждый ряд. Хотя вы определенно можете предпринять некоторые действия, чтобы попытаться решить эту проблему, могут быть причины, по которым это происходит так медленно. 27.03.2010
  • Да, но сначала проверка оборудования - никогда не плохая идея. От тебя я бы перешел к таким вещам, как план запроса на удаление (красиво - показывает триггеры). 27.03.2010

  • 6

    Вы можете удалять небольшие партии, используя цикл while, примерно так:

    DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
    WHILE @@ROWCOUNT > 0
    BEGIN
        DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
    END
    
    27.12.2016

    7

    Если значительный процент таблицы будет соответствовать критериям удаления (около или более 50%), то дешевле создать временную таблицу с записями, которые не будут удаляться (отмените критерии WHERE), усеките исходную таблицу, а затем заново заполнить ее записями, которые должны были храниться.

    DELETE FROM TABLE WHERE ROW_TO_DELETE = 'OK';
    GO
    
    -->
    
    INSERT INTO #TABLE WHERE NOT ROW_TO_DELETE = 'OK';
    TRUNCATE TABLE;
    INSERT INTO TABLE (SELECT * FROM #TABLE);
    GO
    
    26.11.2020

    8

    вот решение вашей проблемы.

    DECLARE @RC AS INT
    SET @RC = -1
    
    WHILE @RC <> 0
    BEGIN
        DELETE TOP(1000000) FROM [Archive_CBO_ODS].[CBO].[AckItem] WHERE [AckItemId] >= 300
        SET @RC = @@ROWCOUNT
        --SET @RC = 0
    END
    
    08.12.2016
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]