На мой вопрос есть 2 части.
- Есть ли способ указать начальный размер базы данных в PostgreSQL?
- Если нет, как вы справляетесь с фрагментацией, когда база данных растет со временем?
Недавно я перешел с MSSQL на Postgres, и одна из вещей, которые мы делали в мире MSSQL при создании базы данных, заключалась в указании начального размера базы данных и журнала транзакций. Это уменьшило фрагментацию и увеличило производительность, особенно если «нормальный» размер базы данных известен заранее.
Производительность моей базы данных падает с ростом размера. Например, рабочая нагрузка, которую я выполняю, обычно занимает 10 минут. По мере роста базы данных это время увеличивается. Выполнение VACUUM, VACUUM FULL и VACUUM FULL ANALYZE, по-видимому, не решает проблему. Что решает проблему с производительностью, так это остановка базы данных, фрагментация диска и последующее выполнение VACUUM FULL ANALYZE, возвращающее производительность моего теста к первоначальным 10 минутам. Это заставляет меня подозревать, что фрагментация - это то, что причиняет мне боль.
Я не смог найти никаких ссылок на резервирование табличного пространства / пространства базы данных в Postgres. Либо я использую неправильную терминологию и, следовательно, ничего не нахожу, либо есть другой способ смягчения фрагментации файловой системы в Postgres.
Есть указатели?
Решение
Предоставленные ответы помогли подтвердить то, что я начал подозревать. PostgreSQL хранит базу данных в нескольких файлах, и это позволяет базе данных расти, не беспокоясь о фрагментации. Поведение по умолчанию состоит в том, чтобы упаковать эти файлы до краев с табличными данными, что хорошо для таблиц, которые редко изменяются, но плохо для таблиц, которые часто обновляются.
PostgreSQL использует MVCC для одновременного доступа к данным таблицы. Согласно этой схеме каждое обновление создает новую версию строки, которая была обновлена (это может быть с помощью отметки времени или номера версии, кто знает?). Старые данные не сразу удаляются, а помечаются для удаления. Фактическое удаление происходит при выполнении операции VACUUM.
Как это связано с коэффициентом заполнения? Коэффициент заполнения таблицы по умолчанию, равный 100, полностью упаковывает страницы таблицы, что, в свою очередь, означает, что на странице таблицы нет места для хранения обновленных строк, то есть обновленные строки будут помещены на страницу таблицы, отличную от исходной строки. Это плохо сказывается на производительности, как показывает мой опыт. Поскольку мои сводные таблицы обновляются очень часто (до 1500 строк / сек), я решил установить коэффициент заполнения 20, то есть 20% таблицы будут для вставленных данных строки и 80% для данных обновления. Хотя это может показаться чрезмерным, большой объем пространства, зарезервированный для обновленных строк, означает, что обновленные строки остаются на той же странице, что и оригинал, и страница таблицы не заполнена к тому времени, когда демон autovacuum запускается для удаления устаревших строк.
Чтобы «починить» свою базу данных, я сделал следующее.
- Установите коэффициент заполнения моих сводных таблиц равным 20. Вы можете сделать это во время создания, передав параметр в CREATE TABLE или по факту через ALTER TABLE. Я выполнил следующую команду plpgsql:
ALTER TABLE "my_summary_table" SET (fillfactor = 20);
- Выдан VACUUM FULL, поскольку при этом записывается совершенно новая версия файла таблицы и, следовательно, косвенно записывается новый файл таблицы с новым коэффициентом заполнения .
Перезапуская свои тесты, я не вижу снижения производительности, даже когда база данных настолько велика, насколько мне нужно, чтобы она была с миллионами строк.
TL; DR - фрагментация файлов не была причиной, это была фрагментация табличного пространства. Это можно уменьшить, изменив коэффициент заполнения таблицы в соответствии с вашим конкретным вариантом использования.