Скорость усечения Postgresql

Мы используем Postgresql 9.1.4 качестве нашего сервера db. Я пытался ускорить работу своего тестового набора, поэтому я уставился на профилирование db немного, чтобы точно посмотреть, что происходит. Мы используем database_cleaner для обрезания таблиц в конце тестов. ДА Я знаю, что транзакции быстрее, я не могу использовать их в определенных обстоятельствах, поэтому я не заинтересован в этом.

То, что Я ЕСМЬ, связано с тем, почему TRUNCATION занимает так много времени (дольше, чем использование DELETE) и почему для моего CI-сервера требуется EVEN LONGER.

Прямо сейчас, локально (на Macbook Air) полный комплект тестов занимает 28 минут. Завершение журналов каждый раз, когда мы обрезаем таблицы … т.е.:

 TRUNCATE TABLE table1, table2 -- ... etc 

для выполнения усечения требуется более 1 секунды. Приведение журналов на нашем CI-сервере (Ubuntu 10.04 LTS) занимает всего 8 секунд, чтобы обрезать таблицы, а assembly занимает 84 минуты.

Когда я переключился на страtagsю :deletion , моя локальная assembly заняла 20 минут, а сервер CI снизился до 44 минут. Это существенная разница, и я действительно сдулся, почему это может быть. Я настроил БД на сервере CI, он имеет 16-гигабайтный системный RAM, 4gb shared_buffers … и SSD. Все хорошее. Как это возможно:

а. что он ТАК намного медленнее, чем мой Macbook Air с 2 гб RAM
б. что TRUNCATION намного медленнее DELETE, когда postgresql docs явно заявляют, что он должен быть намного быстрее.

Есть предположения?

Это появилось несколько раз в последнее время, как в SO, так и в почтовых списках PostgreSQL.

TL; DR для двух последних пунктов:

(a) Большие shared_buffers могут быть причиной того, что TRUNCATE медленнее на сервере CI. Также может быть виновата другая конфигурация fsync или использование ротационных носителей вместо SSD.

(b) TRUNCATE имеет фиксированную стоимость, но не обязательно более медленную, чем DELETE , плюс она делает больше работы. См. Подробное объяснение, которое следует ниже.

ОБНОВЛЕНИЕ: из этой публикации возникла значительная дискуссия о производительности pgsql . Смотрите эту тему .

ОБНОВЛЕНИЕ 2: Усовершенствования добавлены к 9.2beta3, которые должны помочь в этом, см. Этот пост .

Подробное объяснение TRUNCATE vs DELETE FROM :

Я не знаю эксперта по этой теме, я понимаю, что TRUNCATE имеет почти фиксированную стоимость за таблицу, а DELETE – не менее O (n) для n строк; хуже, если есть foreign keys, ссылающиеся на удаляемую таблицу.

Я всегда предполагал, что фиксированная стоимость TRUNCATE была ниже, чем стоимость DELETE на почти пустой таблице, но это совсем не так.

TRUNCATE table; делает больше, чем DELETE FROM table;

Состояние базы данных после TRUNCATE table выглядит так же, как если бы вы запускали:

  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (Только 9.0+, см. Сноску)

… хотя, конечно, TRUNCATE фактически не достигает своих эффектов с помощью DELETE и VACUUM .

Дело в том, что DELETE и TRUNCATE делают разные вещи, поэтому вы не просто сравниваете две команды с одинаковыми результатами.

DELETE FROM table; позволяет сохранять мертвые ряды и раздуваться, позволяет индексировать несущие записи, не обновляет статистику таблицы, используемую планировщиком запросов, и т. д.

TRUNCATE дает вам совершенно новую таблицу и индексы, как если бы они были только CREATE ed. Это похоже на то, что вы удалили все записи, переиндексировали таблицу и сделали VACUUM FULL .

Если вам не все равно, если в таблице остался crud, потому что вы собираетесь пойти и заполнить его снова, вам может быть лучше использовать DELETE FROM table; ,

Поскольку вы не используете VACUUM вы обнаружите, что мертвые строки и индексные записи накапливаются как раздувание, которое необходимо отсканировать, а затем игнорировать; это замедляет все ваши запросы. Если ваши тесты фактически не создают и не удаляют все те данные, которые вы не заметите или не заботитесь, и вы всегда можете сделать VACUUM или два VACUUM пути через ваш тестовый прогон, если вы это сделаете. Лучше, пусть агрессивные настройки автовакуума гарантируют, что автовакуум сделает это для вас на заднем плане.

Вы все еще можете TRUNCATE всех своих таблиц после TRUNCATE всего тестового набора, чтобы убедиться, что во многих прогонах нет эффектов. На 9.0 и новее, VACUUM (FULL, ANALYZE); глобально на столе, по крайней мере, так хорошо, если не лучше, и это намного проще.

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

DELETE FROM table; очень дешево для небольших столов без f / k refs

Чтобы DELETE все записи из таблицы без ссылок на foreign keys, все Pg должны выполнить последовательное сканирование таблицы и установить xmax встречающихся кортежей. Это очень дешевая операция – в основном линейное чтение и полулинейная запись. AFAIK ему не нужно касаться индексов; они продолжают указывать на мертвые кортежи, пока они не будут очищены более поздним VACUUM который также отмечает, что блоки в таблице содержат только мертвые кортежи как свободные.

DELETE становится дороже, если есть много записей, если есть много ссылок на foreign keys, которые необходимо проверить, или если вы подсчитаете последующую VACUUM (FULL, ANALYZE) table; чтобы соответствовать эффектам TRUNCATE пределах стоимости вашего DELETE .

В моих тестах здесь DELETE FROM table; как правило, в 4 раза быстрее, чем TRUNCATE на 0,5 мс против 2 мс. Это тестовая БД на SSD, работающая с fsync=off потому что мне все равно, потеряю ли я все эти данные. Конечно, DELETE FROM table; не делает все той же работы, и если я буду следить за VACUUM (FULL, ANALYZE) table; это намного дороже 21 мс, поэтому DELETE – это только победа, если мне не нужна настоящая таблица.

TRUNCATE table; делает намного более фиксированную стоимость работы и домашнего хозяйства, чем DELETE

Напротив, TRUNCATE должен выполнять большую работу. Он должен выделять новые файлы для таблицы, ее таблицу TOAST, если таковые имеются, и каждый индекс, который имеет таблица. Заголовки должны быть записаны в эти файлы, и системные каталоги могут также нуждаться в обновлении (не уверен в этом, не проверял). Затем он должен заменить старые файлы на новые или удалить старые, и должен обеспечить, чтобы файловая система догнала изменения с помощью операции синхронизации – fsync () или аналогичной – обычно сбрасывает все буферы на диск , Я не уверен, пропускается ли синхронизация, если вы работаете с опцией (data-eating) fsync=off .

Недавно я узнал, что TRUNCATE должен также очистить все буферы PostgreSQL, связанные со старой таблицей. Это может потребовать нетривиального количества времени с огромными shared_buffers . Я подозреваю, что на сервере CI он медленнее.

Баланс

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

Следовательно, вам может быть лучше делать DELETE FROM table; ,

Примечание: в БД до 9.0 CLUSTER table_id_seq ON table; ANALYZE table; CLUSTER table_id_seq ON table; ANALYZE table; или VACUUM FULL ANALYZE table; REINDEX table; VACUUM FULL ANALYZE table; REINDEX table; будет более близким к TRUNCATE . VACUUM FULL impl изменился на гораздо лучший в 9.0.

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

Связанный вопрос: 30 таблиц с несколькими строками – TRUNCATE – самый быстрый способ их опорожнения и сбросить прикрепленные последовательности?

Также рассмотрите эту проблему и этот запрос:

https://github.com/bmabey/database_cleaner/issues/126

https://github.com/bmabey/database_cleaner/pull/127

Также этот stream: http://archives.postgresql.org/pgsql-performance/2012-07/msg00047.php

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

Несколько альтернативных подходов к рассмотрению:

  • Создайте пустую базу данных со статическими данными «fixture» в ней и запустите в ней тесты. Когда вы закончите, просто снимите базу данных, которая должна быть быстрой.
  • Создайте новую таблицу под названием «test_ids_to_delete», которая содержит столбцы для имен таблиц и идентификаторов первичного ключа. Обновите логику удаления, чтобы вместо этого вставить имена идентификаторов / таблиц в эту таблицу, что будет намного быстрее, чем запуск удалений. Затем напишите сценарий, чтобы запустить «офлайн», чтобы фактически удалить данные, либо после завершения всего тестового прогона, либо в одночасье.

Первый – подход «чистой комнаты», а последний означает, что некоторые тестовые данные будут сохраняться в базе данных дольше. «Грязный» подход с автономными удалениями – это то, что я использую для тестового набора с примерно 20 000 тестов. Да, иногда возникают проблемы из-за наличия «дополнительных» тестовых данных в базе данных dev, но иногда. Но иногда эта «грязность» помогла нам найти и устранить ошибку, потому что «беспорядок» лучше имитировал ситуацию в реальном мире, так что подход чистой комнаты никогда не будет.

В последнее время я столкнулся с подобной проблемой, то есть:

  1. Время запуска набора тестов, в котором используется DatabaseCleaner, широко варьировалось между различными системами с сопоставимым оборудованием,
  2. Изменение страtagsи DatabaseCleaner для :deletion при условии улучшения 10x.

Коренной причиной медленности была файловая система с журналированием (ext4), используемая для хранения базы данных. Во время операции TRUNCATE демона журналирования (jbd2) использовала ~ 90% емкости дискового ввода-вывода. Я не уверен, является ли это ошибкой, краевым случаем или нормальным поведением в этих обстоятельствах. Это объясняет, однако, почему TRUNCATE был намного медленнее DELETE – он генерировал намного больше записей на диске. Поскольку я не хотел использовать DELETE, я прибегал к установке fsync=off и этого было достаточно, чтобы смягчить эту проблему (безопасность данных в этом случае не была важна).

  • Как реализовать отношения «многие ко многим» в PostgreSQL?
  • Ограничить максимальное количество строк таблицы sqlite
  • Подключение к mssql с помощью pdo через php и linux
  • Можем ли мы обновить значения первичных ключей таблицы?
  • Spring Security: кодирование пароля в БД и в applicationContext
  • разница между первичным ключом и уникальным ключом
  • Сохранять общий запрос в виде столбца?
  • Что такое нормализация (или нормализация)?
  • Почему у вас нет внешнего ключа в полиморфной ассоциации?
  • Уже есть объект, названный в базе данных
  • Войдите в базу данных, используя log4j
  • Interesting Posts

    В C ++ / CLI, как я объявляю и вызываю функцию с параметром ‘out’?

    Ошибка Android – Open Failed ENOENT

    Использование C # MethodInvoker.Invoke () для приложения с графическим интерфейсом … это хорошо?

    Полиморфизм: зачем использовать «List list = new ArrayList» вместо «ArrayList list = new ArrayList»?

    Windows 7: Событие 55 Структура файловой системы на диске повреждена и непригодна для использования

    Как члены classа C ++ инициализируются, если я не делаю этого явно?

    Окончание строки – char c = 0 vs char c = ‘\ 0’

    Являются ли @ManagedBeans устаревшими в JavaEE6 из-за @Named в CDI / Weld?

    Сильный воинский маршрут не маршрутизирует правильно

    Как автоматически перезапустить фоновый процесс linux, если он не работает?

    Как отключить подсказки для пароля при выполнении git push / pull?

    Исключить значение с помощью команд в команде bash

    Как заставить Windows 10 отображать текстовое поле пароля при запуске, а не изображение с указанием времени и даты?

    Cocoa Autolayout: обход содержимого с приоритетом сжатия содержимого

    конвертировать символ на дату * быстро * в R

    Давайте будем гением компьютера.