- PVSM.RU - https://www.pvsm.ru -

В облачном мире PostgreSQL возникает много вопросов. Какую конфигурацию выбрать для старта кластера? Сколько оперативной памяти и ядер CPU нужно под мою базу данных? Нужны ли под такой профиль нагрузки высокочастотные процессоры? Какое должно быть соотношение RAM к Storage в кластере? Хватает ли ресурсов и на системные службы, и на кэширование запросов? Не переплачиваю ли я?
Всем привет! Меня зовут Гришин Александр, я руководитель по развитию продуктов хранения данных в Selectel, отвечаю за объектное S3-хранилище [1] и облачные базы данных [2]. В этой статье я поделюсь своими практическими рекомендациями и ориентирами по планированию использования ресурсов кластера в PostgreSQL — в зависимости от типа и профиля нагрузки, размера данных и характера доступа к ним. Погнали!
Эта информация основывается на моем опыте, не претендует на единственно верный и всеобъемлющий подход. Рассчитываю, что она пригодится инженерам, разработчикам и СТО для оптимального выбора стартовой конфигурации облачного кластера PostgreSQL и, как следствие, ускорения работы приложения.
Используйте навигацию, если не хотите читать текст целиком:
→ Особенности работы PostgreSQL с ресурсами кластера [3]
→ Базовая формула [4]
→ Как узнать размер индексов [5]
→ Типовые профили нагрузки и рекомендации для них [6]
→ Слоты логической репликации [7]
→ Мониторинг и метрики [8]
→ Заключение [9]
Давайте поверхностно разберем, как PostgreSQL использует доступную ему память:
shared_buffers — основной кэш приложения PostgreSQL,page cache — основной кэш операционной системы,work_mem,temp_buffers — память на сортировки и join-операции (умножается на каждую операцию и соединение),maintenance_work_mem — для VACUUM, CREATE INDEX и прочего,
shared_buffers — это часть оперативной памяти, управляемая самой PostgreSQL. В ней хранятся страницы таблиц и индексов, которые PostgreSQL активно использует. PostgreSQL сначала читает данные в shared_buffers, прежде чем возвращать их в ответ на запрос. При записи изменения сначала идут в shared_buffers, и только потом синхронизируются на диск (через WAL и чекпоинты). Не стоит отдавать shared_buffers 100% от общей RAM кластера. Помним, что память потребуется под системный кеш, рабочую память (work_mem и прочее) и прочие нужды.
page cache — файловый кэш операционной системы, в который попадают данные автоматически, если они читаются или пишутся через обычные файловые вызовы (например, read(), write()). PostgreSQL не знает напрямую, что хранится в page cache, но использует его косвенно, потому что все операции чтения/записи идут через файловую систему. Page Cache может хранить как данные PostgreSQL, так и всех остальных системных служб в ОС.
Оба эти уровня используют одну и ту же оперативную память. Если задать слишком большой shared_buffers, не останется памяти под Page Cache. Это может ухудшить производительность, особенно при больших последовательных чтениях.
Следует следить за количеством подключений. 100 соединений с work_mem = 64МБ — это +6 ГБ памяти только под join-операции! Неожиданно? Особенно если вам на старте казалось, что хватит всего 4 ГБ RAM на весь кластер.
RAM распределяется между множеством компонентов услуги DBaaS, от операционной системы и дополнительных сервисов услуги типа мониторинга, до резервного копирования, high availability и т.д. — важно учитывать это при планировании.
Есть у PostgreSQL и особенности работы с CPU, которые важны при стартовом выборе конфигурации кластера. PostgreSQL однопоточная на уровне выполнения запроса, то есть один SQL-запрос = один процесс = одно ядро.
Это значит, что масштабирование происходит по количеству параллельных запросов, а не внутри одного запроса (за исключением параллельных запросов). Отсюдо вытекает, что высокочастотные ядра (CPU линейки HighFreq) могут быть особенно полезны в нагрузке, где важна минимальная латенси одиночных запросов.
В PostgreSQL поддерживаются параллельные запросы для SELECT с JOIN, AGGREGATE, INDEX SCAN, но только если это разрешено планом выполнения и параметрами (parallel_setup_cost, parallel_tuple_cost, max_parallel_workers_per_gather).
PostgreSQL может задействовать несколько воркеров в рамках одного запроса. В таких случаях будет важна не только частота, но и количество CPU.
PostgreSQL выигрывает от CPU с высокой тактовой частотой и от многопоточности при большом числе параллельных клиентов. Но каждый отдельный запрос в большинстве случаев выполняется в одном потоке.
Возможно, самый важный элемент системы почти при любом профиле нагрузки для PostgreSQL — дисковая подсистема. Обычно в PostgreSQL не вся база помещается в память и именно диск становится узким местом.
Особенно сильно медленные диски влияют на работу аналитики и BI. Такой профиль нагрузки включает в себя тяжелые вложенные JOIN, GROUP BY, сортировки, математические функции и временные таблицы. Все это неизбежно обращается к диску. Кроме того:
work_mem — тут тоже параметры диска будут напрямую влиять на отклик;autovacuum, checkpoints, WAL-логирование также создают дисковую нагрузку, и медленные накопители могут стать бутылочным горлышком.Использование сетевых дисков (InfiniBand, Fibre Channel, Ethernet и т. д.) гарантированно увеличивает latency. Несмотря на то что «скорость света высока», сигнал проходит не мгновенно. Физику не обманешь. По моему опыту, локальные SSD NVMe-диски дают кратный прирост практически во всех сценариях.
Производительность диска: IOPS, bandwidth, latency становятся самыми важными параметрами практически при любом профиле нагрузки в PostgreSQL. Медленные диски гарантированно станут бутылочным горлышком вашей информационной системы.

Тонкая настройка любой системы, в том числе СУБД, в первую очередь зависит от профиля нагрузки. Если вы не знаете его, не беда. Основываясь на своем опыте работы в Selectel, я подготовил общие рекомендации по RAM:

Рекомендуемое соотношение количества RAM к объему активных данных.
Например, если вы храните 1 ТБ данных, но активно работаете с 300-500 ГБ (например, заказы за последние 12 месяцев), вам потребуется как минимум 75-250 ГБ RAM, чтобы держать все горячее в памяти.
Общую рекомендацию по CPU я бы свел к тому, что чем больше ядер и выше их частота, тем быстрее PostgreSQL выполняет вычисления. Но учитываем, что не вся нагрузка масштабируется линейно по потокам.
Особенно параметры CPU критичны для:
JOIN, GROUP BY, с сортировками,Если вы не знаете профиль нагрузки, просто используйте самые быстрые локальные диски, которые вам доступны. Это подойдет для любого кейса.
Нужно интенсивное чтение больших объемов данных в OLAP-профиле? Берите быстрые локальные диски. Нагрузка гибридна и распределена по многим файлам и индексам? Берите быстрые локальные диски. Важна низкая латенси доступа к данным и быстрая обработка каждого конкретного запроса? Однозначно берите быстрые локальные диски. Главная задача при их выборе — они не должны стать узким местом в работе вашей системы.
Чтобы определить, сколько места занимают индексы в вашей базе данных, PostgreSQL предоставляет несколько полезных запросов. Ниже представлю пару примеров с пояснениями.
SELECT
pg_size_pretty(SUM(pg_relation_size(i.indexrelid))) AS total_index_size
FROM
pg_index i
JOIN pg_class c ON c.oid = i.indrelid
WHERE
c.relkind = 'r';
Этот запрос суммирует размеры всех индексов (pg_relation_size(i.indexrelid)) для всех обычных таблиц (relkind = 'r'). Он покажет общий объем, который индексы занимают на диске. Это полезно, если вы хотите прикинуть, сколько RAM потребуется, чтобы кэшировать их в памяти.

Пример вывода запроса из BI Superset с общим размером индексов. Это дополнительные 20ГБ, которые ноде кластера СУБД нужно будет как-то обработать при запросах.
SELECT
relname AS table,
pg_size_pretty(pg_indexes_size(relid)) AS index_size
FROM pg_catalog.pg_statio_user_tables;
Этот запрос показывает, сколько места занимают индексы по каждой отдельной пользовательской таблице (pg_statio_user_tables). Это особенно полезно, если вы хотите определить «тяжелые» таблицы, где основную нагрузку создают именно индексы.
Пример вывода:
| table | index_size |
| orders | 3 GB |
| customers | 1.5 GB |
| products | 800 MB |
Таким образом, если вы понимаете, что ваше приложение будет активно работать именно с таблицей orders, вам стоит заложить дополнительные 3 ГБ RAM на ноду только под ее индексы в shared_buffers.
Рекомендую периодически запускать эти запросы и отслеживать рост индексов. Это поможет вовремя пересмотреть конфигурацию RAM и настроить автоматическое обслуживание — например,
REINDEX, если размер индекса раздувается из-за частых обновлений.
Примеры: интернет-магазины, CRM, SaaS, микросервисы и т. д.
Характеристики:
SELECT, INSERT, UPDATE, DELETE,Рекомендации по объему RAM в таблице основаны на принципе, что RAM должна покрывать как минимум активные индексы и часто используемые данные, чтобы избежать чтения с диска. В OLTP-нагрузке важнее обеспечить:
| Размер данных на диске | Предполагаемый размер активных данных | Рекомендуемый размер RAM |
| 100-200 ГБ | 80-120 ГБ | 64-96 ГБ |
| 300-500 ГБ | 200-300 ГБ | 128-192 ГБ |
| >1 ТБ | >500 ГБ | >256 ГБ |
Рекомендации по CPU
Для такого профиля нагрузки обычно не нужно много ядер, поэтому:
В OLTP RAM особенно важна для кэширования индексов, чтобы ускорить поиск и избежать чтения с диска. Размер индексов может составлять от 20% до 40% от объёма таблиц, в зависимости от структуры базы и количества вторичных индексов. Не нужно много ядер CPU, но может быть важна их частота. Диски потребуются локальные SSD NVMe.
Примеры: КХД, отчеты по продажам, витрины данных, агрегации, дашборды.
Характеристики:
GROUP BY, JOIN, ORDER BY,Рекомендации по RAM
work_mem — от 16 до 128 МБ на соединение (зависит от числа одновременных запросов).Рекомендации по CPU
Для такого профиля нагрузки важно количество ядер CPU. И тут, как говорится, чем больше, тем лучше! Многоуровневые вложенные запросы с использованием математических функций будут гарантированно класть ресурсы CPU-кластера на полочку.
parallel query в PostgreSQL позволяет эффективно использовать такие процессоры.В OLAP-кластерах особенно важна пропускная способность диска (IOPS) и SSD. Размеры аналитических данных сейчас доходит до десятков ТБ (и больше!). Будет технически сложно покрыть нужным объемом RAM кластер такого размера.
Преимущество локальных дисков пропадает с увеличением размера кластера до десятков терабайт. Сетевой latency для такого кластера становится не критичным и может быть выгодно выбирать быстрые сетевые диски.
Примеры: e-commerce с real-time аналитикой, ERP, логистические платформы.
Характеристики:
Рекомендации по RAM
Материализованное представление — это сохраненный результат запроса, обновляемый вручную или по расписанию. Оно помогает ускорить повторные тяжелые запросы без постоянной нагрузки на живые таблицы. Например, если у вас есть тяжелый отчет по продажам, его можно сохранить в materialized view и обновлять раз в час — это снизит нагрузку на основную таблицу заказов.
Рекомендации по CPU
Тут сложнее всего дать хорошие рекомендации. Кластер, подходящий под разные профили нагрузки, скорее всего, не подходит ни под одну из них. Однако если уж так стоит задача, вам потребуется сразу и большое количество ядер для использования многопоточности в OLAP, и высокая частота для OLTP профиля нагрузки. Дорого? Да.
Еще раз подумайте о том, чтобы разнести нагрузку на разные кластеры.
Может появиться желание тонко затюнить каждую ноду. Мастер выбрать с небольшим количеством высокочастотных ядер CPU и локальными дисками, а реплику с большим количеством обычных ядер CPU и на сетевых дисках. Но я строго не рекомендую так делать!
По моему опыту ноды кластера должны быть гомогенны по характеристикам, т. к. на эти параметры завязано множество других ситуаций, обрабатываемых кластером СУБД. Например, switchover: роль ноды кластера может переключиться в любой момент жизни системы, и это штатная ситуация. В таком случае у вас получатся не оптимальные характеристики нод, обслуживающих приключившуюся нагрузку. Реплика начнет отставать от мастера. Это рано или поздно приведет к разбалансировке всей системы и аварийной ситуации. Узкое место на одной ноде — это всегда узкое место для всего кластера.
Важным для HTAP профиля нагрузки будет вертикальное разделение нагрузки по ролям в кластере, однако это может привести к распуханию мастера. Такое может случится когда долгий SELECT мешает применять WAL-журнал, и Postgres вынужден прервать запрос. Так же, не нужно тюнить характеристики каждой отдельной ноды. Характеристики нод кластера обязательно должны быть гомогенны. Еще раз подумайте о том чтобы разнести разные нагрузки на разные кластера.
Примеры: исторические логи, старые транзакции, бэкапы.
Рекомендации
На старте это самый нетребовательный к ресурсам профиль нагрузки.
Логическая репликация — мощный механизм PostgreSQL, позволяющий передавать изменения из одной базы в другую (например, для интеграции с Kafka, CDC, миграций, BI-систем и т.д.). Но важно помнить, что она потребляет дополнительные CPU-ресурсы — как на отправляющей стороне, так и на принимающей.
Примерные ориентиры по нагрузке на CPU во время логической репликации:
| Изменения в секунду на 1 слот | Примерная нагрузка на CPU |
| до 1 000 | ~0.1 ядра |
| 5 000 – 10 000 | ~0.3-0.5 ядра |
| 20 000 и более | ~1 ядро |
Что влияет на нагрузку:
INSERT, UPDATE, DELETE),jsonb, text, bytea),pgoutput, wal2json, и т. д.),Закладывайте примерно по одному ядру CPU на каждый активный логический слот, особенно если поток изменений интенсивный.
Однако логическая репликация — это не только нагрузка на процессор, но и на диск, особенно если подписчик временно не может получать данные (например, из-за сетевого разрыва, перегрузки или проблем на принимающей стороне). Рассмотрим упрощенный механизм такой ситуации:
pg_wal закончится).pg_wal. Обязательный минимум — 3-5 ГБ запаса на каждый активный логический слот, лучше больше.wal_keep_size (или wal_keep_segments в старых версиях) до безопасного уровня.pg_replication_slots, pg_stat_replication, pg_wal_lsn_diff.Если вы понимаете, что будете использовать слоты логической репликации, то обязательно стоит заложить одно ядро CPU на каждый слот репликации. И еще 20–25% к емкости диска под
pg_wal, если понимаете, что соединение будет нестабильным. Обязательно настройте мониторинг состояния слотов и алерты на заполнение диска и заранее продумайте действия и реакцию на такие инциденты.
Измеряешь, значит управляешь. Поэтому я считаю, что мало правильно выбрать количество и соотношение ресурсов на старте. Более того — этот выбор не имеет никакого смысла, если вы не настроили мониторинг и не измеряете состояние системы в реальном времени.
В первую очередь, могу рекомендовать следующие метрики (но не ограничиваясь ими):
| Метрика | Что показывает | Комментарий |
cache hit ratio
|
% запросов, попавших в кэш | >99% — отлично;
95-99 — нормально; <95 — терпимо; <90% — признак острой нехватки RAM |
pg_stat_activity
|
Текущие подключения и их состояние | Помогает в диагностике зависаний, блокировок |
pg_stat_statements
|
Часто выполняемые запросы, их время и ресурсы | Оптимизация узких мест |
pg_buffercache
|
Какие таблицы/индексы реально в кэше | Проверка гипотез о горячих данных |
pg_stat_io
(доступно для PostgreSQL c 16-й версии) |
Подробная статистика по чтению/записи блоков | Анализ IO-нагрузки |
pg_stat_bgwriter
|
Активность background writer | Оценка внутренней активности кэша и записи |
В нашей услуге метрики можно отслеживать как в панели управления [11], так и в собственной системе мониторинга, настроив трансляцию метрик в формате prometheus. Подробнее ознакомиться с этой частью функциональности можно в нашей технической документации [12].
cache hit ratio= 1 — (blks_read / blks_hit) — метрика, которую я рекомендую отслеживать в первую очередь. Она способна дать понимание, правильно ли вы выбрали соотношение RAM/Storage.
Небольшое резюме:
Есть еще один важный аспект, который я сознательно не затронул в рамках статьи — производительность сетевой подсистемы. Это не значит, что она не важна. В ряде случаев узкий канал и медленные интерфейсы могут стать большой проблемой.
Однако чтобы не перегружать материал, я решил выделить эту тему в отдельную публикацию. Если вам интересно продолжение, напишите об этом в комментариях, и я обязательно расскажу, почему сеть — это не только про пропускную способность, но и про задержки, флап и надежность, особенно в распределенных архитектурах.
Все вышесказанное — мой личный опыт и мое личное мнение, которые, надеюсь, помогут вам оптимизировать выбор и соотношение ресурсов кластера PostgreSQL. Однако если уж начистоту, я считаю, что нет лучшего рецепта, чем найм DBA-инженера себе в штат. Очень рекомендую!
А для получения максимально возможной по производительности конфигурации в виде готовой облачной услуги рекомендую рассмотреть облачные базы данных на выделенном сервере [13]. Подробнее о том, как и почему мы первые в РФ сделали подобную услугу, я уже рассказывал в статье на Хабре [14].
Автор: GrishinAlex
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/bazy-danny-h/420929
Ссылки в тексте:
[1] объектное S3-хранилище: https://selectel.ru/services/cloud/storage/?utm_source=habr.com&utm_medium=referral&utm_campaign=storage_article_sql-hardware_270525_content
[2] облачные базы данных: https://selectel.ru/services/cloud/managed-databases/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_sql-hardware_270525_content
[3] Особенности работы PostgreSQL с ресурсами кластера: #1
[4] Базовая формула: #2
[5] Как узнать размер индексов: #3
[6] Типовые профили нагрузки и рекомендации для них: #4
[7] Слоты логической репликации: #5
[8] Мониторинг и метрики: #6
[9] Заключение: #7
[10] Облачных баз данных на Enterprise-оборудовании с высокочастотными процессорами: https://selectel.ru/services/cloud/managed-databases/high-freq/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_sql-hardware_270525_content
[11] в панели управления: https://my.selectel.ru/
[12] в нашей технической документации: https://docs.selectel.ru/cloud/managed-databases/postgresql/monitoring/#export-metrics-in-prometheus-format
[13] облачные базы данных на выделенном сервере: https://selectel.ru/services/cloud/managed-databases/vdedicated/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_sql-hardware_270525_content
[14] в статье на Хабре: https://habr.com/ru/companies/selectel/articles/880338/
[15] Источник: https://habr.com/ru/companies/selectel/articles/912996/?utm_source=habrahabr&utm_medium=rss&utm_campaign=912996
Нажмите здесь для печати.