Продолжаем выжимать максимум из PostgreSQL

Kate

Administrator
Команда форума
В апреле этого года мы, команда производительности из Postgres Professional, совместно с коллегами из Selectel решили протестировать несколько дистрибутивов PostgreSQL и узнать, как они себя поведут на разных архитектурах. С результатами можно ознакомиться в этой статье, но, как сразу было верно отмечено читателями, там был один важный косяк – мы не сравнили производительность ванильного PostgreSQL с применением всем известных настроек по улучшению производительности и Postgres Pro Enterprise из коробки as is. Терпеть такое не было решительно никакой возможности, поэтому сегодня будет продолжение истории и ответ на важный для многих вопрос: «А есть ли у нашего форка хоть какое-то преимущество перед бесплатной ваниллой?» Или мы просто накатили общеизвестный конфиг и занимаемся импортозаместительным переклеиванием наклеек?

Чтобы сохранить общую картину, валидность с предыдущими результатами и не изобретать свой велосипед, мы взяли тесты pgbench, которыми с нами любезно поделились коллеги из Selectel. Всё красиво и аккуратно расписано в их статье, поэтому не будем останавливаться на этом моменте. Единственное – мы немного усложнили себе задачу и поставили цель выявить ключевые настройки, которые дают максимальный прирост производительности. Также был добавлен тест при использовании компрессии на уровне хранения данных (CFS). В остальном всё максимально близко к тому, что делали в Selectel.

Тестовый стенд​

В тестах Selectel использовалось несколько конфигураций bare metal серверов, так как среди прочих там была и задача проверить производительность различных платформ. Мы же взяли только один сервер со следующими характеристиками:

  • Процессор: Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz (2 Sockets)
  • 128 RAM Kingston DDR4 1TB
  • Диск: iSCSI-storage 1TB (Selectel использовали в тестах SSD-диски с поддержкой NVMe и PCIe Gen 4)
В качестве ОС использовалась Debian GNU/Linux 11 (bullseye) с ядром 5.15.116-1-pve

СУБД для сравнения были выбраны:

  • Postgres Pro Enterprise 16.2.1
  • PostgreSQL 16.2
Для стабильной работы тестов и воспроизводимости результатов мы применили несколько довольно бесхитростных настроек, о которых рассказывал наш коллега Михаил Жилин в своём докладе.

Подготовка и сценарии тестов​

Пропустим стандартные процессы установки из готовых пакетов и первичной настройки СУБД PostgreSQL и перейдём сразу к сути. Для тестов pgbench создаём пользователя и базы:

CREATE ROLE pgbench with login password 'pgbench';
CREATE DATABASE pgbench WITH owner = pgbench;
CREATE TABLESPACE cfs LOCATION '/u02/cfsdir' WITH (compression=true);
CREATE DATABASE pgbench2 WITH TABLESPACE cfs owner = pgbench;
Генерируем тестовые данные с множителем scale=10000. У нас объём БД составил 146 Gb без сжатия и 13 Gb при включённом CFS.

lab@lab:~$ pgbench -i -s 10000 pgbench
dropping old tables...
NOTICE: table "pgbench_accounts" does not exist, skipping
NOTICE: table "pgbench_branches" does not exist, skipping
NOTICE: table "pgbench_history" does not exist, skipping
NOTICE: table "pgbench_tellers" does not exist, skipping
creating tables...
generating data (client-side)...
1000000000 of 1000000000 tuples (100%) done (elapsed 1341.08 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done in 1725.21 s (drop tables 0.00 s, create tables 0.01 s, client-side generate 1344.56 s, vacuum 0.95 s, primary keys 379.70 s).
И чтобы всё по-честному, и результаты воспроизводились, перед каждой серией тестов БД пересоздавалась с нуля.

Тестовые конфигурации​

Никаких тайн, рассказываем всё как на духу. У нас было три тестовых конфигурации. Конфигурация 1, известная под кодовым именем «Postgres Pro Enterprise 16.2.1 из коробки с дефолтным конфигом» включала в себя только то, что разворачивалось по дефолту:

max_connections = 1600
shared_buffers = 257915MB # 25% of RAM
effective_cache_size = 3GB
maintenance_work_mem = 64478MB
max_wal_size = 4GB
min_wal_size = 2GB
checkpoint_completion_target = 0.9
effective_cache_size = 773745MB # 75% of RAM
wal_buffers = 16MB
default_statistics_target = 100
Конфигурация 2, она же «Postgres, тюнингованный по советам лучших интернет-сомелье». Абсолютно одинаковый набор параметров, который прописывался в конфиг ванильного PostgreSQL 16.2, Postgres Pro Enterprise 16.2.1 и Postgres Pro Enterprise 16.2.1 с включённым CFS. Конфиг длинный, поэтому спрячем под спойлер.

Конфиг под спойлером
archive_timeout = 1200
autovacuum_analyze_scale_factor = 0.6
autovacuum_max_workers = 6
autovacuum_naptime = 1s
autovacuum_vacuum_cost_delay = 2ms
autovacuum_vacuum_cost_limit = 1000
autovacuum_vacuum_scale_factor = 0.6
autovacuum_work_mem = 1GB
bgwriter_delay = 10ms
bgwriter_flush_after = 0
bgwriter_lru_maxpages = 4000
bgwriter_lru_multiplier = 10
checkpoint_completion_target = 0.9
checkpoint_timeout = 30min
commit_delay = 100
commit_siblings = 5
datestyle = 'iso, mdy'
default_statistics_target = 1000
default_text_search_config = 'pg_catalog.english'
default_toast_compression = lz4
dynamic_shared_memory_type = posix
effective_cache_size = 792315195kB
effective_io_concurrency = 100
from_collapse_limit = 30
fsync = on
huge_pages = on
join_collapse_limit = 30
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
log_filename = 'PostgreSQL-%a.log'
logging_collector = on
log_line_prefix = '%m [%p] %q%u@%d '
log_rotation_age = 1d
log_rotation_size = 1GB
log_timezone = 'Europe/Moscow'
log_truncate_on_rotation = on
maintenance_work_mem = 2GB
max_connections = 1000
max_parallel_workers_per_gather = 0
max_replication_slots = 30
max_wal_senders = 30
max_wal_size = 32GB
min_wal_size = 10GB
port = 5432
random_page_cost = 1.1
shared_buffers = 257915MB # 25% of RAM
ssl=off
synchronous_commit = on
timezone = 'Europe/Moscow'
vacuum_cost_limit = 2000
wal_buffers = -1
wal_compression = lz4
wal_level = replica
wal_sync_method = fdatasync
work_mem = 1MB
И Конфигурация 3, представляющая собой версию Конфигурации 2, дополненную параметрами, которые есть только в Postgres Pro Enterprise.
autoprepare_for_protocol = simple
autoprepare_threshold = 2
generic_plan_fuzz_factor = 0.9
log2_num_lock_partitions = 8
lwlock_shared_limit = 16
slru_buffers_size_scale = 6

Параметры тестирования​

Ничего экстраординарного мы не делали, просто использовали стандартные методы:
  1. Тестирование производилось в рамках локалхоста, чтобы избежать возможное влияние сетевых задержек.
  2. Каждый тест выполнялся 600 секунд.
  3. pgbench использовал простой протокол запросов (параметр -M simple). В принципе, он выбирается по умолчанию, просто мы решили выбирать его явным образом.
  4. Для каждой из семи комбинаций параметров Клиент/Поток последовательно запускались встроенные в pgbench тесты tpcb-like, select-only и simple-update
Итого получается 21 запуск. Также для снижения флуктуаций было выполнено по 3 итерации тестов с усреднением полученных результатов. Вот табличка получившихся комбинаций параметров:
Клиенты (-c)
Потоки (-j)
% потоков pgbench от общего кол-ва CPU Threads
1​
2​
1​
-​
2​
4​
2​
-​
3​
50​
25​
20​
4​
102​
51​
40​
5​
152​
76​
60​
6​
204​
102​
80​
7​
254​
127​
99.9​

Тестирование Конфигурации 1​

Суть тестирования Postgres Pro Enterprise из коробки в том, чтобы найти референтные значения, относительно которых уже можно делать выводы – улучшилась у нас ситуация или внесённый в конфиг улучшайзинг сделал всё только хуже. Поэтому давайте посмотрим на графики и оценим, какие результаты получились в этих трёх тестах.
tpcb-like: Максимальная производительность составила 41877 TPS при тесте для 127 потоков
7f585dba8c0b07c7c3a31dba2a0cd247.png

select-only: Максимальная производительность составила 870776 TPS при тесте для 76 потоков
e99549d2f0733ca623e657564ac5a149.png

simple-update: Максимальная производительность составила 47157 TPS при тесте для 127 потоков
8cd07af2d4cf47b04746010310a6da23.png

Эти значения мы и будем сравнивать с последующими результатами. В качестве обобщения можно считать, что достигнутые показатели будут релевантны и для ваниллы.

Тестирование Конфигурации 2​

Напомню, что в этом тесте мы сравниваем ванильную, Pro-версию и Pro-версию с CFS. Не будем вас томить и сразу переходим к полученным TPS и промежуточным выводам.
tpcb-like

tpcb-like
  • PostgreSQL 16.2 НЕ проигрывает Postgres Pro Enterprise 16.2.1. И даже показывает лучший результат на 3-6%, кроме замера для 127 потоков.
  • Postgres Pro Enterprise 16.2.1 с поддержкой CFS по сравнению с PostgreSQL 16.2 показывает прирост в производительности до 27% на тестах при большом числе потоков.
select-only

select-only
  • PostgreSQL 16.2 НЕ проигрывает Postgres Pro Enterprise 16.2.1 в среднем. Но в зависимости от числа потоков, наблюдаем небольшое преимущество той или иной редакции.
  • Результаты Postgres Pro Enterprise 16.2.1 + CFS почти во всех тестах НЕ лучше, чем у Postgres Pro Enterprise без CFS.
simple-update

simple-update
  • PostgreSQL 16.2 НЕ проигрывает Postgres Pro Enterprise 16.2.1, и показывает лучший результат на 2-3% (кроме теста при 127 потоках).
  • Postgres Pro Enterprise 16.2.1 с поддержкой CFS по сравнению с PostgreSQL 16.2 показывает прирост в производительности до 45% на тестах при большем числе потоков.
Здесь можно сделать следующие обобщающие выводы:
  • Производительность ванильного Postgres в целом находится на уровне сопоставимом с версией Postgres Pro, когда не используется сжатие.
  • Postgres Pro с использованием CFS ощутимо выигрывает у ваниллы на тестах tpcb-like и simple-update.

Тестирование Конфигурации 3​

И переходим к самому интересному – тестам Postgres Pro с параметрами, которых нет в ванильной версии. По аналогии с предыдущими тестами, на диаграммах сравниваются достигнутые различными редакциями TPS.
tpcb-like: Результаты изменились незначительно и аналогичны результатам, полученным на одинаковом наборе настроек.
  • PostgreSQL 16.2 показывает лучший результат на 2-3% в сравнении с Postgres Pro Enterprise.
  • Postgres Pro Enterprise 16.2.1 с поддержкой CFS по сравнению с PostgreSQL 16.2 теперь показывает прирост в производительности до 29%. Прирост в 2%
78071ece731c583884f8169c860947f2.png

select-only: Ванильная версия PostgreSQL 16.2 проигрывает редакциям Postgres Pro Enterprise 16.2.1 / Postgres Pro 16.2.1 + CFS до 37%. При одинаковых настройках производительность была примерно равной.
1adf79ec46510b36aff53891eb851bea.png

simple-update: Результаты сопоставимы с результатами, полученными на одинаковом наборе настроек:
  • PostgreSQL 16.2 в целом показывает сопоставимые результаты с Postgres Pro Enterprise 16.2.1 – небольшой выигрыш 1-2% при меньшем количестве потоков и заметно худший результат при 127 потоках.
  • Postgres Pro 16.2.1 с поддержкой CFS по сравнению с PostgreSQL 16.2 показывает прирост в производительности до 50%.
85c566ed677bcd171e1ada260107bd01.png

Итого:
  1. Ванильный Postgres все еще немного производительнее Postgres Pro на тесте tpcb-like и в целом сопоставим по производительности при тесте simple-update. На select-only тесте Postgres Pro показал производительность на ~37% лучше, чем у ваниллы.
  2. Производительность Postgres Pro с использованием CFS значительно превосходит ванильную: в зависимости от теста, выигрыш составил от 30 до 50%.

Что действительно влияет на производительность?​

Для выявления параметров, которые внесли максимальное влияние, были проведены дополнительные тесты. По их результатам мы выявили двух победителей:
  • autoprepare_for_protocol = simple: этот параметр устанавливает протокол, по которому передаются запросы, обрабатываемые механизмом автоподготовки.
  • autoprepare_threshold = 2: параметр определяет минимальное количество выполнений оператора, после которого он будет подготовлен.
Сводные таблицы производительности Postgres Pro Enterprise относительно PostgreSQL (при использовании конфигурации 2):
Тест
Конфигурация 2
Конфигурация 3
Postgres Pro Enterprise 16.2.1
Postgres Pro Enterprise 16.2.1 + CFS
Postgres Pro Enterprise 16.2.1
Postgres Pro Enterprise 16.2.1 + CFS
tpcb-like
- 3%
+ 27%
- 3%
+ 29%
select-only
- 3%
=​
+ 37%
+ 37%
simple-update
- 3%
+ 45%
- 3%
+ 50%
Cравнение производительности Postgres Pro Enterprise относительно самого себя на Конфигурации 3 относительно Конфигурации 1:
Тест
Postgres Pro Enterprise 16.2.1
Postgres Pro Enterprise 16.2.1 + CFS
tpcb-like
+ 25%​
+ 63%​
select-only
+ 79%​
+ 79%​
simple-update
+ 32%​
+ 74 %​

Загрузка железа во время тестов​

Полученные значения TPS – это хорошо и наглядно, но по уму надо их соотнести с реальной загрузкой железа. Иначе может оказаться, что выигрыш в TPS нивелируется несоразмерным увеличением загрузки железа. Вот, что мы увидели:
  • При тестах tpcb-like и simple-update с аналогичным числом клиентов/потоков, на БД с включенной CFS утилизация CPU выше на 5-10%, но и объем записи на диск кратно меньше (до 4 раз в начале теста). На графиках ниже БД с CFS справа. При тестах без CFS объем дисковой записи большую часть теста превышал 300MB/sec, в то время как при тестах на БД с поддержкой CFS был ~150MB/sec.
    42e5f48dfdb92c4c52ea5c11f1f8bfea.png
  • При текущем паттерне нагрузки (тесты tpcb-like / select-only / simple-update, запускаемые сразу друг за другом) на select-only тестах Postgres обеих редакций в начале теста присутствует просадка производительности. Для примера, график TPS для теста выполненного для 102 потоков:
    2d0129715f5fc64802f3d0c48f3ecf87.png
  • Графики утилизации системных ресурсов показывают, что в начале select-only тестов производится интенсивная запись на диск:
    2508e03e9f0df0be9dc59b66a9decb49.png
Это интересное поведение, поэтому с помощью инструмента pgpro-otel-collector была собрана информация из pg_stat_activity, позволившая выявить типы ожиданий, с которыми связана запись на диск: XactSLRU, WALInsert, WALWrite.
3b6f237e9349eebabd92663890307f33.png

Также отметим, что тесту select-only предшествует тест tpcb-like, транзакции которого добавляют/обновляют случайные строки:
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES :)tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
Соответственно, поскольку фиксация изменений осуществляется только в структуре журнала фиксации транзакций clog, при дальнейшем запуске select-only теста происходит обращение к табличным страницам и проверка статуса ранее выполненных транзакций через обращение к SLRU-кешу. Часть блоков отсутствует в кеше, что приводит к необходимости считывать их с диска. После выяснения статуса транзакций, они записываются в заголовок версии строки в информационные биты (hint bits), что и вызывает интенсивную запись на диск. Кроме того, изменение hint bits журналируется в виде полного образа страницы (FPI), с чем и связаны ожидания записи журнала. Таким образом, тайна высокой нагрузки на запись при запуске select-only теста раскрыта.

Выводы​

  • Если мы что-то протестировали не так или что-то забыли, обязательно напишите в комментариях. Исправимся и дотестируем.
  1. Тюнинг конфигурации Postgres Pro Enterprise (c обобщением на PostgreSQL) и настроек ОС (включение huge_pages, перенос pg_stat_tmp в RAM-диск, ака Конфигурация 2), позволяет достичь следующего прироста производительности по сравнению с настройками Postgres Pro Enterprise / PostgreSQL «из коробки»:
    1. в сценарии tpcb-like – до 63% на тестах при большем числе потоков
    2. в сценарии select-only – до 79%
    3. в сценарии simple-update – до 74%
  2. При одинаковых конфигурациях, настроенных для работы в производительном режиме (Конфигурация 2), Postgres Pro Enterprise 16.2.1 и ванильная версия PostgreSQL 16.2 показывают схожие результаты на всех сценариях тестирования (tpcb-like, select-only, simple-update), с небольшим преимуществом ванильной версии от 2 до 6%. При этом, если Postgres Pro Enterprise используется вместе с CFS, то по сравнению с ваниллой:
    1. в тесте tpcb-like – редакция Enterprise выигрывает до 27% в тестах с большим числом потоков
    2. в тесте select-only – демонстрирует практически такой же результат
    3. в тесте simple-update — показывает прирост в производительности до 45% при большом количестве потоков.
  3. Применение специфичных настроек, имеющихся только в Enterprise-редакции (Конфигурация 3), позволяет Postgres Pro Enterprise 16.2.1 в сравнении с ванильным PostgreSQL 16.2 увеличить производительность в тесте select-only до 37% на тестах при большом числе потоков. А при использовании CFS Enterprise опережает ваниллу во всех тестах.
    1. tpcb-like – выигрывает до 29% на тестах при большом количестве потоков
    2. select-only –- выигрывает до 37% на тестах при большом количестве потоков
    3. simple-update – показывает прирост в производительности до 50% на тестах при большом количестве потоков
  4. Максимальный прирост производительности обеспечили параметры планировщика, имеющиеся только в редакции Postgres Pro Enterprise: autoprepare_for_protocol = simple и autoprepare_threshold = 2.
  5. Заодно найдена интересная особенность поведения SLRU-кэшей при простановке Hint-битов. Тесты tpcb-like / select-only / simple-update запускаются сразу друг за другом в каждой итерации, а в следующей итерации число тредов увеличивается. Такой паттерн нагрузки приводит к тому, что в течение двух минут после начала select-only теста, базы данных обеих редакций изменяют Hint-биты и производят интенсивную запись на диск. Как результат, наблюдается постепенный рост производительности от 10% до 100%.
На этом всё. Сделать какие-то более глубокие метафизические выводы мы оставляем за вами, т.к. наша задача – показать результаты как есть. Остаётся добавить, что методика тестирования и сами тесты доступны всем и каждому, поэтому, если у вас получаются кардинально другие результаты, будем бесконечно рады их обсудить и подумать над причинами.

 
Сверху