Скорость усечения Postgresql
мы используем Postgresql 9.1.4 как сервер БД. Я пытался ускорить свой тестовый набор, поэтому я немного посмотрел на профилирование БД, чтобы точно увидеть, что происходит. Мы используем database_cleaner для усечения таблиц в конце тестов. Да, я знаю, что транзакции быстрее, я не могу использовать их в определенных обстоятельствах, поэтому меня это не волнует.
что меня беспокоит, так это то, почему усечение занимает так много времени (дольше, чем использование DELETE) и почему оно занимает еще больше времени мой сервер CI.
прямо сейчас, локально (на Macbook Air) полный набор тестов занимает 28 минут. Следя за журналами, каждый раз мы усекаем таблицы... т. е.:
TRUNCATE TABLE table1, table2 -- ... etc
для выполнения усечения требуется более 1 секунды. Отслеживание журналов на нашем сервере CI (Ubuntu 10.04 LTS), занимает полные 8 секунд, чтобы усечь таблицы, а сборка занимает 84 минуты.
когда я переключился на :deletion стратегия, моя локальная сборка заняла 20 минут, и сервер CI опустился до 44 минут. Это же значительное разница, и я действительно потрясен, почему это может быть. У меня тюнингованныхthe DB на сервере CI, он имеет 16 ГБ оперативной памяти системы, 4 ГБ shared_buffers... и SSD. Все хорошие вещи. Как это возможно:
a. что это намного медленнее, чем мой MacBook Air с 2 ГБ оперативной памяти
b. это усечение намного медленнее, чем удалить, когда postgresql документыявно что это должно быть намного быстрее.
какие мысли?
4 ответов:
это появилось несколько раз в последнее время, как в so, так и в списках рассылки PostgreSQL.
The TL; DR для последних двух пунктов:
(a)чем больше shared_buffers, тем медленнее усечение на сервере CI. Другая конфигурация fsync или использование вращательных носителей вместо твердотельных накопителей также могут быть виноваты.
(b)
TRUNCATEимеет фиксированную стоимость, но не обязательно медленнее, чемDELETE, плюс он делает больше работы. Смотрите подробное объяснение, которое следует.обновление: A значительная дискуссия по pgsql-performance из этого поста. Смотрите этой теме.
обновление 2: улучшения были добавлены в 9. 2beta3, которые должны помочь в этом, см. этот пост.
подробное описание
TRUNCATEvsDELETE FROM:пока не эксперт по этой теме, Мой понимание заключается в том, что
TRUNCATEимеет почти фиксированную стоимость за стол, в то время какDELETEпо крайней мере O(n) для n строк; хуже, если есть какие-либо внешние ключи, ссылающиеся на удаляемую таблицу.я всегда предполагал, что фиксированная стоимость
TRUNCATEбыло ниже, чем стоимостьDELETEна почти пустом столе, но это совсем не так.
TRUNCATE table;не болееDELETE FROM table;состояние базы данных после
TRUNCATE tableэто почти то же самое, что если бы вы вместо этого побежали:
DELETE FROM table;VACCUUM (FULL, ANALYZE) table;(только 9.0+, см. сноску)... хотя конечно
TRUNCATEфактически не достигает своих эффектов с помощьюDELETEиVACUUM.дело в том, что
DELETEиTRUNCATEделайте разные вещи, поэтому вы не просто сравниваете две команды с одинаковыми результатами.A
DELETE FROM table;позволяет мертвым строкам и раздувать оставаться, позволяет индексам переносить мертвых записи, не обновляет статистику таблицы, используемую планировщиком запросов и т. д.A
TRUNCATEдает вам совершенно новые таблицы и указатели, как если бы они были простоCREATEed. Это как вы удалили все записи, переиндексировали таблицу и сделалиVACUUM FULL.Если вам все равно, если в таблице осталась грязь, потому что вы собираетесь снова ее заполнить, вам может быть лучше использовать
DELETE FROM table;.потому что ты не бежишь
VACUUMвы найдете что мертвые строки и записи индекса накапливаются как вздутие, которое должно быть Отсканировано, а затем проигнорировано;это замедляет все ваши запросы. Если ваши тесты на самом деле не создают и не удаляют все эти данные, которые вы можете не заметить или не заботиться, и вы всегда можете сделатьVACUUMили две части пути через ваш тестовый запуск, если вы это сделаете. Лучше, пусть агрессивные настройки автовакуума гарантируют, что автовакуум делает это за вас в фоновом режиме.вы все еще можете
TRUNCATEвсе ваши таблицы после весь
Брэд, просто чтобы ты знал. Я довольно глубоко изучил очень похожий вопрос.
пожалуйста, Также посмотрите на эту проблему и эту тягу запрос:
https://github.com/bmabey/database_cleaner/issues/126
https://github.com/bmabey/database_cleaner/pull/127
также этот поток:http://archives.postgresql.org/pgsql-performance/2012-07/msg00047.php
Я сожалею, что написал это в качестве ответа, но я не нашел никаких ссылок на комментарии, возможно, потому, что там уже слишком много комментариев.
несколько альтернативных подходов к рассмотрению:
- создайте пустую базу данных со статическими данными "fixture" в ней и запустите тесты в ней. Когда вы закончите, просто удалите базу данных, которая должна быть быстрой.
- создайте новую таблицу с именем "test_ids_to_delete", которая содержит столбцы для имен таблиц и идентификаторов первичных ключей. Обновите логику удаления, чтобы вместо этого вставить идентификаторы / имена таблиц в эту таблицу, что будет намного быстрее, чем выполнение удалений. Потом написать сценарий для запуска "в автономном режиме", чтобы фактически удалить данные, либо после завершения всего тестового запуска, либо в одночасье.
первый-это подход "чистой комнаты", в то время как последний означает, что некоторые тестовые данные будут сохраняться в базе данных дольше. "Грязный" подход с автономным удалением - это то, что я использую для набора тестов с примерно 20 000 тестов. Да, иногда возникают проблемы из-за наличия "дополнительных" тестовых данных в базе данных dev, но иногда. Но иногда это " грязь" помог нам найти и исправить ошибку, потому что" беспорядок " лучше моделировал реальную ситуацию, таким образом, что подход чистой комнаты никогда не будет.
я столкнулся с подобной проблемой в последнее время, т. е.:
- время запуска тестового набора, который использовал DatabaseCleaner, широко варьировалось между различными системами с сопоставимым оборудованием,
- изменение стратегии DatabaseCleaner на
:deletionпри условии ~ 10x улучшение.основной причиной медлительности была файловая система с журналированием (ext4), используемая для хранения базы данных. Во время операции усечения демон ведения журнала (jbd2) использовал ~90% дискового ввода-вывода вместимость. Я не уверен, что это ошибка, крайний случай или на самом деле нормальное поведение в этих обстоятельствах. Это объясняет, однако, почему усечение было намного медленнее, чем удаление - оно генерировало намного больше записей на диск. Как я не хочу, чтобы на самом деле использовать удалить, я обратился к установлению
fsync=offи этого было достаточно, чтобы смягчить эту проблему (сохранность данных не важна в этом случае).
Comments