В прошлый раз мы говорили о холодном и горячем запусках, и тем самым вышли на тему прогрева. Прогрев – это насильный загон блоков в кэш, чтобы следующий запрос читал строки из кэша, а не собирал их с диска. В комментариях привели случай: перед сложным запросом данные читают SELECT-ом, чтобы прогреть кэш.

К счастью, есть более простой способ прогрева. Расширение pg_prewarm делает буквально это: принимает имя таблицы или индекса и насильно загоняет в буферный кэш. Для особых случаев можно указать диапазон блоков, но маловероятно, что это вам пригодится.

В целом прогрев выглядит так:

pg_prewarm('some_schema.users')
/* for table*/

pg_prewarm('some_schema.idx_users_created_at)
/* for index */

Функция возвращает число прочитанных блоков. Поскольку блок в Postgres равен 8 килобайтам, умножьте результат на 8192, чтобы узнать размер таблицы или индекса в байтах (разумеется, есть и другие способы посчитать размер, но это чтобы убить двух зайцев).

Postgres не гарантирует, что все блоки окажутся в памяти. Первые могут быть вытеснены последними, да и вообще – любой запрос может вмешаться и загрузить свои блоки, а ваши выкинуть.

Техника с прогревом спорная, и нужно использвать ее по месту. На мой взгляд, полезно прогревать:

  • небольшие справочные таблицы, которые часто читают. Если таблица небольшая, индексы на нее не действуют, и будет full scan. Если вы часто читаете таблицу, поставьте ее на прогрев. Сюда относятся разные классификаторы, каталоги услуг, категории и так далее.

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

Как прогревать таблицу или индекс по расписанию? Можно задать крон-джоб, который выполняет скрипт pg_prewarm(...) каждый час. Другой вариант – подключить расширение pg_cron. Его нет в поставке Postgres, но оно установлено у всех облачных провайдеров. На линуксе оно ставится из пакета postgresql-16-cron (замените 16 на вашу версию PG).

Примеры:

SELECT cron.schedule(
  'prewarm categories table',
  '0 */1 * * *',
  $sql$pg_prewarm('prod.categories')$sql$
);

SELECT cron.schedule(
  'prewarm users.created_at index',
  '5 */1 * * *',
  $sql$pg_prewarm('prod.idx_users_created_at')$sql$
);

Также рассмотрите опцию shared_buffers – размер буферного кэша. По умолчанию он равен 32 мегабайта, что довольно мало. В боевом режиме его оптимальная величина – 25% от всей памяти сервера. Например, если у вас 8 гигабайт памяти, то размер кэша можно выставить 2 гигабайта. При этом не рекомендуется превышать 40% всей памяти.

Ссылка на документацию.