Добавить в корзинуПозвонить
Найти в Дзене
Postgres DBA

Подробное объяснение пункта «Недостаток shared_buffers»

Из отчётов pgpro_pwr: В течение обоих анализируемых интервалов: Сумма shared_buffers (16 ГБ) + memory_cache (55 ГБ) = 71 ГБ, что существенно превышает физические 62.8 ГБ ОЗУ. Поскольку PostgreSQL читает данные через системные вызовы (pread/pwrite), каждая страница, прочитанная с диска, сначала попадает в OS page cache, а затем копируется в shared_buffers. Таким образом, одна и та же страница данных одновременно находится и в page cache ядра, и в разделяемой памяти PostgreSQL. Это явление называется двойной буферизацией (double buffering). В норме OS page cache может содержать данные, вытесненные из shared_buffers, но при суммарном объёме кэшей > RAM оба кэша конкурируют за ограниченную физическую память. Когда свободной памяти почти нет, ядро ОС вынуждено освобождать место в page cache, вытесняя наименее используемые страницы. Вытесненные страницы теряются для PostgreSQL, даже если они ещё нужны. При последующем обращении к этим данным возникает физическое чтение с диска, несмотря на н
Оглавление

1. Роль shared_buffers и effective_cache_size в PostgreSQL

  • shared_buffers — это область разделяемой памяти, выделяемая PostgreSQL для кэширования страниц данных (блоков размером 8 КБ) непосредственно в адресном пространстве сервера. Когда запросу требуется прочитать данные, сначала проверяется этот кэш. Если страница найдена (cache hit), физическое чтение с диска не требуется. Если не найдена (cache miss), страница считывается с диска в shared_buffers, вытесняя при необходимости менее используемые страницы по алгоритму clock-sweep.
  • effective_cache_size — это оценочный параметр, сообщающий планировщику запросов общий объём памяти, доступный для кэширования данных на уровне операционной системы (OS page cache) плюс shared_buffers. Он не выделяет память, а влияет на выбор плана: при большом значении планировщик склонен считать, что индексы и данные часто находятся в памяти, и предпочитает индексные сканы.

2. Фактические значения на сервере

Из отчётов pgpro_pwr:

  • shared_buffers = 16 ГБ (2058112 блоков по 8 КБ)
  • effective_cache_size = 48 ГБ (6174336 блоков по 8 КБ)
  • Общий объём ОЗУ сервера ≈ 62.8 ГБ (определено из memory_total в системной статистике)

3. Наблюдаемое состояние памяти ОС (memory_cache и memory_free)

В течение обоих анализируемых интервалов:

  • memory_cache (OS page cache) ≈ 55 ГБ
  • memory_free < 1.1 ГБ (менее 5%)

Сумма shared_buffers (16 ГБ) + memory_cache (55 ГБ) = 71 ГБ, что существенно превышает физические 62.8 ГБ ОЗУ.

4. Механизм двойной буферизации

Поскольку PostgreSQL читает данные через системные вызовы (pread/pwrite), каждая страница, прочитанная с диска, сначала попадает в OS page cache, а затем копируется в shared_buffers. Таким образом, одна и та же страница данных одновременно находится и в page cache ядра, и в разделяемой памяти PostgreSQL. Это явление называется двойной буферизацией (double buffering). В норме OS page cache может содержать данные, вытесненные из shared_buffers, но при суммарном объёме кэшей > RAM оба кэша конкурируют за ограниченную физическую память.

5. Вытеснение страниц (page eviction) и его последствия

Когда свободной памяти почти нет, ядро ОС вынуждено освобождать место в page cache, вытесняя наименее используемые страницы. Вытесненные страницы теряются для PostgreSQL, даже если они ещё нужны. При последующем обращении к этим данным возникает физическое чтение с диска, несмотря на наличие shared_buffers.

В анализируемом случае:

  • Проблемный запрос 8811732978066195686 обрабатывает сотни миллионов блоков, сканируя большие таблицы.
  • Большая часть рабочего набора не умещается в shared_buffers (16 ГБ), поэтому активно используется OS page cache.
  • Однако из-за конкуренции за память между shared_buffers и page cache, страницы, необходимые запросу, вытесняются из OS page cache, вызывая повторные физические чтения.

6. Падение hit ratio проблемного запроса во второй день

  • 2026-04-07: hit ratio запроса = 96% → почти все данные находились либо в shared_buffers, либо в OS page cache.
  • 2026-04-08: hit ratio запроса = 78% → каждое четвёртое обращение к блоку требовало физического чтения.

Причины ухудшения:

  • Во второй день запрос выполнялся с иными параметрами или над другим набором данных, что изменило паттерн доступа к памяти.
  • Возросла конкуренция за память со стороны других процессов (например, autovacuum, другие ETL-запросы).
  • Вытеснение страниц из OS page cache стало более агрессивным из-за общего дефицита памяти.

7. Влияние на производительность

Физические чтения с диска vdb, утилизация которого уже составляла ~100%, приводят к росту задержек (ожидания DataFileRead). Рост времени выполнения запроса на 72% во второй день прямо коррелирует с увеличением физических чтений на 63% и падением hit ratio.

8. Почему effective_cache_size не помогает

Параметр effective_cache_size = 48 ГБ вводит планировщик в заблуждение, заставляя считать, что в памяти доступно 48 ГБ для кэширования данных, хотя фактически OS page cache испытывает острую нехватку памяти и вытесняет страницы. В результате планировщик может выбирать планы, предполагающие быстрый доступ к индексам (Index Scan), которые в реальности требуют случайных чтений с перегруженного диска, усугубляя проблему.

Вывод

Превышение суммы shared_buffers + memory_cache над физическим объёмом ОЗУ создаёт условия для двойной буферизации и вытеснения страниц из OS page cache. Это приводит к падению hit ratio, росту физических чтений и, как следствие, увеличению времени выполнения запросов при уже предельно нагруженной дисковой подсистеме. Без детальной статистики по таблицам невозможно точно определить, какие именно страницы вытесняются, но общая картина согласуется с наблюдаемыми метриками.