Найти в Дзене
Postgres DBA

PG_EXPECTO: Анализ инцидента снижения производительность СУБД PostgreSQL. Часть-2:Инфраструктура.

СУБД — это музыка. Инфраструктура — концертный зал. Поиск трещину в стене, из-за которой весь оркестр фальшивит. В первой части исследования проведен анализ инцидента снижения производительности PostgreSQL на уровне СУБД. Были идентифицированы проблемные запросы и паттерны . Однако, как показывает системный подход, деградация производительности сложных систем редко имеет единственную причину. Симптомы, наблюдаемые на уровне базы данных, часто являются следствием патологий в фундаментальных слоях инфраструктуры. Данная статья представляет собой вторую, системную часть расследования. Её цель — перейти от наблюдаемых симптомов (медленные транзакции, блокировки) к установлению корневых причин на уровне операционной системы и аппаратного обеспечения. Проведена декомпозицию инцидента, последовательно анализируя метрики дисковой подсистемы , управления памятью и обработки прерываний ЦПУ. В итоге видно, как аномальная задержка записи на одном из накопителей (w_await >5 мс) при низкой утилиза
Оглавление

СУБДОМИНАНТА — это музыка. Инфраструктура — концертный зал. Необхолимо найти трещинк в стене, из-за которой весь оркестр фальшивит.

Визуализация каскадного отказа инфраструктуры. Диск vdb (медленная запись), переполненный кэш памяти и рои прерываний CPU создают системный contention, который деградирует производительность СУБД.
Визуализация каскадного отказа инфраструктуры. Диск vdb (медленная запись), переполненный кэш памяти и рои прерываний CPU создают системный contention, который деградирует производительность СУБД.
GitHub - pg-expecto/pg_expecto: Комплекс pg_expecto для статистического анализа производительности и нагрузочного тестирования СУБД PostgreSQL

Часть-1:СУБД.

Предисловие

В первой части исследования проведен анализ инцидента снижения производительности PostgreSQL на уровне СУБД. Были идентифицированы проблемные запросы и паттерны .

Однако, как показывает системный подход, деградация производительности сложных систем редко имеет единственную причину. Симптомы, наблюдаемые на уровне базы данных, часто являются следствием патологий в фундаментальных слоях инфраструктуры.

Данная статья представляет собой вторую, системную часть расследования. Её цель — перейти от наблюдаемых симптомов (медленные транзакции, блокировки) к установлению корневых причин на уровне операционной системы и аппаратного обеспечения.

Проведена декомпозицию инцидента, последовательно анализируя метрики дисковой подсистемы , управления памятью и обработки прерываний ЦПУ.

В итоге видно, как аномальная задержка записи на одном из накопителей (w_await >5 мс) при низкой утилизации, агрессивная политика кэширования, приведшая к дефициту свободной оперативной памяти, и лавина аппаратных прерываний образовали каскад взаимосвязанных проблем. Именно этот каскад и проявился для пользователей и приложений как критическое замедление работы СУБД.

Этот кейс служит практической иллюстрацией важности полностековой диагностики (Full-Stack Diagnostics), где глубокий анализ СУБД дополняется не менее тщательным исследованием среды её выполнения.

1.Сводный анализ узких мест инфраструктуры

1.1. Наиболее вероятное узкое место

Основная проблема: Диск vdb с высокой задержкой записи (w_await > 5мс)

Рейтинг проблем по критичности:

  1. Диск vdb - самый критичный компонент
  2. Недостаток памяти - вторичная проблема
  3. Высокая корреляция кэш-запись на vdc - симптоматичная, но менее критичная
  4. Высокая корреляция cs-in CPU - следствие, а не причина

1.2. Анализ взаимосвязей компонентов

Первичная проблема: Диск vdb

Данные:

  • w_await достигает 2.72 мс (максимум)
  • 62.3% наблюдений имеют задержку записи > 5 мс (ALARM)
  • Несмотря на низкую утилизацию (util 2.41-5.67%), задержки аномально высокие
  • aqu_sz (длина очереди) до 0.23, что не объясняет высокие задержки при низкой утилизации

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

  • Проблемы с аппаратным обеспечением диска
  • Конфликт доступа к контроллеру/шине
  • Неоптимальные настройки файловой системы

Вторичная проблема: Недостаток памяти

Парадокс:

  • ALARM: Свободной RAM < 5% в >50% наблюдений
  • НО: Свопинг отсутствует (swap_si = swap_so = 0)
  • Кэш памяти очень высокий (~57-59 ГБ из 64 ГБ RAM)

Вывод: Память не "недостаточна", а чрезмерно агрессивно используется для кэширования. Система предпочитает кэшировать данные (cache до 59 ГБ) вместо освобождения памяти для приложений. Это признак:

  • Неоптимальной политики управления памятью (vm.swappiness)
  • Возможно, СУБД запрашивает большие регионы памяти, но не использует их активно

Третичная проблема: Неэффективный кэш (vdc)

Корреляция cache-w/s = 0.9197 и cache-wMB/s = 0.9566 означает:

  • Увеличение кэша сопровождается увеличением операций записи
  • Кэш не снижает нагрузку на диск, а коррелирует с ней - это аномалия
  • Возможная причина: данные, записываемые на vdc, проходят через кэш, но кэш не помогает снизить нагрузку (например, потому что это последовательная запись или запись без последующего чтения)

1.3. Гипотеза влияния на СУБД

Сценарий развития инцидента:

  1. Фоновая проблема: Диск vdb имеет аппаратные или конфигурационные проблемы, приводящие к высокой задержке записи при относительно низкой нагрузке.
  2. Инициирующее событие: СУБД начинает интенсивные операции записи (например, checkpoint, массовые обновления, WAL-записи) на vdb.
  3. Каскадный эффект:
  4. Высокая задержка записи на vdb → замедление commit транзакций
  5. Процессы СУБД, ожидающие записи, блокируются или работают медленно
  6. Очередь запросов к СУБД растет
  7. Усугубляющие факторы:
  8. Память: Хотя свопинга нет, низкая свободная память может ограничивать буферы СУБД
  9. Кэш: Неэффективное использование кэша для vdc может указывать на неправильное распределение нагрузки между дисками
  10. CPU: Высокая корреляция cs-in (0.9439) может быть следствием проблем с диском - прерывания от устройств ввода-вывода вызывают переключения контекста

Конкретное влияние на СУБД:

  • Замедление WAL (Write-Ahead Log): Если vdb используется для журнала транзакций, задержки записи приведут к накоплению незакоммиченных транзакций
  • Blocking и deadlocks: Процессы, ожидающие записи, могут удерживать блокировки
  • Checkpoint stalls: Если checkpoint не успевает записывать "грязные" страницы из-за медленного диска
  • Connection pool exhaustion: Долгие транзакции занимают соединения, новые запросы не могут получить соединение

1.4. Рекомендации для немедленного анализа

Проверить:

  1. Диск vdb:
  2. SMART-статус диска
  3. Параметры монтирования (noatime, nodiratime, barrier)
  4. Настройки планировщика ввода-вывода
  5. Конкуренция за ресурсы с другими дисками на том же контроллере
  6. Распределение данных СУБД:
  7. Что хранится на vdb vs vdc? (Возможно, WAL на vdb, данные на vdc)
  8. Соответствие нагрузки характеристикам дисков
  9. Настройки памяти:
  10. Значение vm.swappiness
  11. Параметры кэширования (vm.dirty_ratio, vm.dirty_background_ratio)
  12. Настройки shared_buffers СУБД

Наиболее вероятный корень проблемы: Аппаратная или конфигурационная проблема с диском vdb, усугубленная неоптимальным распределением нагрузки между дисками и агрессивным кэшированием, которое не приносит пользы для рабочей нагрузки СУБД.

2.Анализ взаимосвязи памяти и дисковых операций. Анализ парадокса: Высокий кэш + высокие задержки записи

2.1. Почему при высоком кэше наблюдаются высокие задержки записи на vdb?

Это кажущийся парадокс имеет несколько технических объяснений:

a) Разделение функций кэша: read-cache vs write-back

Linux кэш (page cache) выполняет две основные функции:

· Read-cache: Кэширование прочитанных данных для последующего быстрого доступа

· Write-back buffer: Буферизация данных перед записью на диск ("грязные страницы")

Проблема: Cистема, судя по данным, использует кэш преимущественно как read-cache, а не как эффективный write-back буфер.

b) Анализ корреляций для vdb:

· buff-w/s = 0.5227 - высокая корреляция между буферами и операциями записи

· cache-wMB/s = 0.5159 - высокая корреляция между кэшем и объемом записи

Интерпретация: Кэш действительно участвует в операциях записи, но не снижает их количество, а скорее сопровождает их. Это может указывать на:

· Синхронные записи: СУБД использует O_DIRECT или fsync, обходящие кэш

· Последовательная запись больших объемов: Данные не подходят для эффективного кэширования

· Вытеснение "грязных" страниц: Кэш переполнен read-данными, оставляя мало места для write-буферов

c) Техническая причина высоких задержек:

1. Двойная работа диска: Диск vdb должен одновременно:

o Обслуживать операции записи из кэша (write-back)

o Читать данные для наполнения кэша (read-ahead)

2. Некорректные настройки кэширования:

# Проблемные параметры (гипотетически):

vm.dirty_ratio = 40% (слишком высоко) → кэш накапливает много "грязных" данных

vm.dirty_background_ratio = 10% → фоновые записи запускаются поздно

vm.dirty_expire_centisecs = 3000 (30 сек) → данные долго лежат в кэше

2.2. Могла ли память быть перегружена кэшированием?

Да, это вероятный сценарий:

Механизм проблемы:

1. Система агрессивно кэширует данные СУБД:

  • СУБД выполняет полное сканирование таблиц
  • Данные загружаются в кэш (57-59 ГБ)
  • Эти данные редко переиспользуются

2. Кэш вытесняет буферы записи:

  • Память заполняется read-кэшем
  • Для "грязных страниц" (dirty pages) остается мало места
  • Система вынуждена чаще сбрасывать данные на диск

3. Статистика подтверждает:

memory_cache: 57-59 ГБ (90%+ от 64 ГБ RAM)

memory_buff: 89-104 МБ (ничтожно мало)

Вывод: Система оптимизирована под чтение в ущерб записи.

Почему свопинг отсутствует?

  • СУБД использует shared memory (не вытесняется)
  • Кэш может быть быстро освобожден при необходимости
  • Но освобождение кэша требует времени и создает дополнительную нагрузку на диск

2.3. Влияние на работу СУБД

a) Увеличение времени фиксации транзакций:

-- Типичный сценарий PostgreSQL:

BEGIN;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;  -- 1. Изменение в shared_buffers

UPDATE accounts SET balance = balance + 100 WHERE id = 2;  -- 2. Изменение в shared_buffers

COMMIT;  -- 3. Запись WAL на диск с задержкой 5+ мс

Результат: Каждая транзакция задерживается на 5+ мс только на фиксации.

b) Каскадные проблемы с checkpoint:

checkpoint_timeout = 5min  → checkpoint каждые 5 минут

checkpoint_completion_target = 0.9  → должен завершиться за 4.5 минуты

НО: При задержке записи 5 мс:

- 10 ГБ "грязных" страниц / (0.016 ГБ/с) = 625 секунд (10.4 минуты)

- Checkpoint не успевает → накапливается WAL → диск переполняется

c) Блокировки и deadlocks:

1. Блокировка на уровне строк:

  • Транзакция A блокирует строку 1
  • Транзакция B ждет строку 1
  • Commit транзакции A занимает 5+ мс
  • Транзакция B ждет дольше → возможен таймаут

2. Блокировка autovacuum:

  • Autovacuum блокирует таблицу
  • Пользовательские запросы ждут
  • Autovacuum медленно пишет на диск → блокировка длится долго

d) Проблемы с репликацией:

-- Для физической репликации:

primary: WAL записывается с задержкой

replica: Получает WAL с задержкой → репликация отстает

e) Эффект домино для приложения:

1. Запись в БД занимает 100 мс вместо 10 мс

2. Connection pool исчерпывается (все соединения заняты)

3. Очередь запросов растет

4. Таймауты приложения

5. Пользователи видят ошибки

2.4. Рекомендации по диагностике

Проверить настройки памяти:

# Текущие настройки

sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_expire_centisecs

sysctl vm.swappiness

# Статистика кэша

cat /proc/meminfo | grep -E "Dirty|Writeback|Cached"

Оптимизировать для рабочей нагрузки СУБД:

# Для рабочей нагрузки с интенсивной записью:

sysctl -w vm.dirty_ratio=10

sysctl -w vm.dirty_background_ratio=5

sysctl -w vm.dirty_expire_centisecs=1000

sysctl -w vm.swappiness=1

Перераспределить дисковую нагрузку:

Предположительно:

- vdb: Медленный диск (HDD) → используется для WAL (ошибка!)

- vdc: Быстрее диск (SSD?) → используется для данных

Рекомендация:

- WAL разместить на самом быстром диске (vdc)

- Данные разместить на vdb

Ключевой вывод: Проблема носит системный характер - неправильная настройка взаимодействия между памятью, кэшем и дисками привела к каскадной деградации производительности СУБД, где высокая задержка записи на vdb стала критическим узким местом.

3.Анализ поведения дисков vdb и vdc. Анализ парадокса: Низкая утилизация + высокая задержка записи.

3.1. Почему при низкой утилизации (util 2-6%) высокая задержка записи (w_await >5 мс)?

Это классический случай проблемы с качеством обслуживания, а не с количественной перегрузкой. Рассмотрим возможные причины:

a) Аппаратная природа диска vdb

Гипотеза: vdb - HDD (магнитный диск), в то время как vdc - SSD/NVMe.

Доказательства:

vdb: w_await = 1.95-2.72 мс (в среднем), пики >5 мс

vdc: w_await = 1.06-1.88 мс (стабильно ниже)

Для HDD время доступа (seek time + rotational latency):

· Seek time: 3-15 мс

· Rotational latency: 2-8 мс(при 7200 RPM = 8.3 мс полный оборот)

· Итого: 5-23 мс на операцию

Вывод: Среднее w_await 2.72 мс - это как раз характерно для HDD с небольшими последовательными операциями.

b) Случайный доступ vs Последовательный доступ

Анализ размеров запросов:

vdb: wareq_sz = 3.77-5.94 КБ (средний размер запроса записи)

vdc: wareq_sz = 7.53-9.21 КБ (крупнее запросы)

Проблема: Мелкие случайные записи на HDD:

· Каждый 4КБ запрос требует полного позиционирования головки

· При последовательном доступе HDD дает 100-200 МБ/с, при случайном - 0.5-2 МБ/с

· Ваш vdb показывает 15-19 МБ/с записи - это больше похоже на случайный доступ

c) Конкуренция чтения/записи

vdb: r/s = 8.4-11.28 операций/сек (чтение)

w/s = 14-16 операций/сек (запись)

Итого: ~25 IOPS

Для HDD 7200 RPM максимальные IOPS случайного доступа: 75-100 IOPS.

Казалось бы: 25 IOPS < 75 IOPS, почему задержки?

Ответ: Конкуренция между чтением и записью:

· Чтение: позиционирование в одной области диска

· Запись: позиционирование в другой области

· Head thrashing: Головка диска постоянно "метается" между областями

3.2. Проблема с очередями (aqu_sz до 0.23) или планировщиком?

a) Анализ глубины очереди (aqu_sz)

vdb: aqu_sz = 0.09-0.23 (средняя длина очереди)

Это критически мало! Означает:

  • В среднем очередь содержит 0.1-0.2 запроса
  • Диск простаивает между запросами

Проблема: Не параллельность, а последовательная обработка

Сравнение с оптимальным:

· Для SSD: aqu_sz < 1 - норма (низкая задержка)

· Для HDD: aqu_sz 4-8 оптимально для параллелизации позиционирования

Текущая ситуация: Один запрос → позиционирование → запись → завершение → следующий запрос. Нет конвейеризации.

b) Планировщик ввода-вывода

Возможные планировщики и их влияние:

1. CFQ (Completely Fair Queuing) - по умолчанию для HDD:

o Создает очереди для каждого процесса

o Проблема: Приоритизация может создавать "голодание" одних процессов

2. Deadline:

o Гарантирует время обслуживания

o Лучше для СУБД

3. NOOP:

o Простая FIFO очередь

o Подходит только для SSD

4. Kyber / BFQ (более новые):

o Пропускная способность + задержки

Проблема: Возможно, используется CFQ с некорректными настройками:

· low_latency включен → приоритет отдается задержке, но снижается пропускная

· Неправильные slice_idle, quantum параметры

c) Проверка гипотезы "одиночные запросы"

Данные iostat показывают:

-2

Расчет: При 16 операциях/сек и aqu_sz=0.05:

  • Среднее время обслуживания = aqu_sz / (w/s) = 0.05/16 = 0.003125 сек = 3.125 мс
  • Фактический w_await ≈ 1.8 мс (чтение задержек меньше расчетного!)

Вывод: Есть расхождение.

Возможно:

1. w_await измеряется только для завершенных запросов

2. Некоторые запросы имеют экстремально высокую задержку (более 5 мс), что влияет на статистику

3.3. Влияние на СУБД в зависимости от назначения vdb

Сценарий A: vdb используется для WAL (журнал транзакций)

Критичность: Высокая.

WAL требует:

  • Минимальной задержки записи (каждая транзакция ждет commit в WAL)
  • Последовательной записи (но в вашем случае размер запросов мал - 8.6 КБ)

Конкретные проблемы:

-- Каждая транзакция будет задерживаться:

BEGIN;

UPDATE table SET column = value WHERE id = 1;

-- Здесь происходит:

-- 1. Запись в shared_buffers (RAM, быстро)

-- 2. Запись WAL на диск (ждет 5+ мс!)

COMMIT; -- Занимает 5+ мс вместо <1 мс

Эффект домино:

1. Увеличение latency транзакций: Каждая транзакция +5 мс

2. Ограничение TPS (Transactions Per Second):

Максимум: 1000 мс / 5 мс = 200 транзакций/сек (теоретически)

Реально: 50-100 транзакций/сек с учетом других задержек

3. Проблемы с репликацией:

  • Primary: WAL пишется медленно
  • Replica: отстает в применении WAL
  • Risk of replication lag > допустимого

Сценарий B: vdb используется для данных (табличные пространства)

Критичность: Средняя-Высокая

Конкретные проблемы:

1. Checkpoint stalls:

-- Checkpoint должен записать "грязные" страницы

-- При задержке 5 мс на 4КБ страницу:

-- 1 ГБ данных = 262144 страниц

-- Время записи: 262144 * 0.005 = 1310 секунд = 22 минуты!

-- Фактически checkpoint будет тормозить систему

2. Autovacuum проблемы:

  • Autovacuum пишет мертвые кортежи на диск
  • Медленная запись → autovacuum занимает больше времени
  • Блокировка таблиц дольше

3. Производительность запросов UPDATE/INSERT:

  • После заполнения shared_buffers, страницы вытесняются на диск
  • Медленная запись → очередь на запись

Сценарий C: vdb используется для TEMP/сортировок

Критичность: Средняя

Проблемы:

  • Сортировка больших наборов данных использует временные файлы
  • Медленный диск → медленная сортировка
  • Запросы с ORDER BY, DISTINCT, JOIN будут выполняться дольше

3.4. Дифференциальная диагностика

Проверить назначение дисков:

# Определить точки монтирования

lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE

# Для PostgreSQL

psql -c "SHOW data_directory;"

psql -c "SHOW log_directory;"

psql -c "SHOW archive_command;"

Проверить планировщик:

# Для vdb

cat /sys/block/vdb/queue/scheduler

# Пример вывода: [noop] deadline cfq

# Квадратные скобки показывают активный планировщик

Рекомендуемые настройки для HDD:

# Для HDD под нагрузкой СУБД

echo deadline > /sys/block/vdb/queue/scheduler

# Увеличить очередь

echo 256 > /sys/block/vdb/queue/nr_requests

# Для CFQ можно настроить приоритет

echo 1 > /sys/block/vdb/queue/iosched/low_latency

3.5. Заключение и рекомендации

Возможная корневая причина: vdb - HDD с высоким временем позиционирования, обслуживающий мелкие случайные операции записи с недостаточной глубиной очереди для эффективного планирования.

Немедленные действия:

1. Проверить тип диска: smartctl -a /dev/vdb

2. Изменить планировщик на deadline: echo deadline > /sys/block/vdb/queue/scheduler

3. Увеличить глубину очереди: echo 512 > /sys/block/vdb/queue/nr_requests

4. Оптимизировать размеры запросов СУБД:

  • Для PostgreSQL: wal_writer_flush_after, bgwriter_lru_maxpages
  • Увеличить shared_buffers для уменьшения частоты записи

Стратегическое решение:

· Перенести WAL на SSD (vdc, если это SSD)

· Использовать vdb только для данных, где задержка менее критична

· Рассмотреть RAID 1+0 для HDD или переход на SSD

Итог: Даже при низкой утилизации задержка в 5+ мс для WAL-диска может парализовать СУБД с высокой нагрузкой на запись. Проблема не в перегрузке диска, а в фундаментальном несоответствии характеристик HDD требованиям рабочей нагрузки СУБД.

4. Анализ CPU и прерываний

Анализ корреляции прерываний и переключений контекста (cs-in = 0.9439)

4.1. Могли ли частые прерывания и переключения создавать contention на CPU при низкой загрузке?

Да, абсолютно! Это классическая ситуация "скрытого contention" при низкой утилизации CPU.

Механизм возникновения contention:

Низкая утилизация (us+sy < 10%) НЕ означает отсутствие contention, потому что:

1. Прерывания имеют высший приоритет: Когда происходит прерывание, CPU немедленно прерывает текущую работу

2. Низкая утилизация ≠ низкая активность: CPU может быть "системно" занят, даже если не показывает высокие us/sy

3. Cache line contention: Прерывания инвалидируют кэш-линии, что замедляет последующее выполнение

Цифровой анализ метрик:

system_in (прерывания): 15,236 - 17,311 в секунду

system_cs (переключения): 8,589 - 9,552 в секунду

cpu_us (user): 7-8%

cpu_sy (system): 2%

cpu_id (idle): 89-91%

Расчет:

· 17,311 прерываний/сек = одно прерывание каждые 57.8 микросекунд

· 9,552 переключений/сек = одно переключение каждые 104.7 микросекунд

Вывод: CPU постоянно переключается между обработкой прерываний и пользовательскими процессами, даже при 90% idle.

Почему это создает contention:

1. Время обработки прерывания: ~2-10 микросекунд каждое

2. Инвалидация кэша: После обработки прерывания L1/L2 кэш содержит данные обработчика прерываний, а не данные СУБД

3. TLB flush: Частые переключения контекста могут инвалидировать TLB (Translation Lookaside Buffer)

4.2. Влияние на производительность СУБД

a) Увеличение задержки обработки запросов:

Без прерываний:

Запрос СУБД: 100 мс чистого CPU времени

С прерываниями (17K/сек):

- Каждые 58 мкс прерывание (2-10 мкс обработка)

- Инвалидация кэша (10-50 нс восстановления)

- Запрос СУБД: 100 мс + 3,448 прерываний × 15 мкс = 151.7 мс (+51% времени)

b) Проблемы с блокировками (locks/latches):

// Типичный сценарий в СУБД:

spinlock_acquire(&lock);  // 1. Захватываем спинлок

// 2. ПРЕРЫВАНИЕ! CPU переключается на обработчик

// 3. Другой процесс пытается захватить тот же спинлок

// 4. Первый процесс возвращается, но lock уже "горячий"

// 5. Увеличение contention на спинлоках

Конкретные проблемы в PostgreSQL:

1. Buffer manager locks: BufMappingLock, BufferContentLock

2. WAL locks: WALWriteLock, ControlFileLock

3. Transaction locks: ProcArrayLock, XidGenLock

c) Эффект "ровной" (flat) производительности:

При contention из-за прерываний:

- Увеличение числа CPU ядер НЕ дает линейного ускорения

- 32-ядерная система может вести себя как 16-ядерная

- Scaling efficiency может упасть до 50%

d) Проблемы с NUMA:

Если прерывания привязаны к определенным ядрам:

Ядро 0: Обработка всех прерываний сети/диска

Ядра 1-31: Процессы СУБД

Но: данные нужно перемещать между NUMA-нодами → увеличение задержки

4.3. Какие процессы/устройства генерировали прерывания?

а) Дисковые прерывания (наиболее вероятно)

Данные указывают на проблемы с vdb:

vdb: w/s = 14-16 операций/сек (запись)

r/s = 8-11 операций/сек (чтение)

Всего: ~25 IOPS

Но каждая операция может генерировать 2+ прерываний:

1. Прерывание от контроллера диска

2. Прерывание от DMA контроллера

3. Прерывание от планировщика (если используется AIO)

Расчет для vdb:

25 IOPS × 2 прерывания/операция = 50 прерываний/сек

Это только 0.3% от общего числа (17,311)!

b) Сетевые прерывания (очень вероятно)

СУБД обычно генерирует сетевую активность:

  • Клиентские соединения
  • Репликация
  • Мониторинг
  • Бэкапы

Типичные значения:

  • 1 Гбит/с сеть: ~81,000 прерываний/сек при полной загрузке
  • 10 Гбит/с: до 1,000,000 прерываний/сек

В данном случае: 17,311 прерываний/сек соответствует ~200 Мбит/с сетевой нагрузке.

c) Таймерные прерывания (timer interrupts)

По умолчанию: 250 Hz (250 прерываний/сек на ядро)

text

32 ядра × 250 Hz = 8,000 прерываний/сек

Это ~46% от ваших 17,311!

d) MSI/MSI-X прерывания

Современные устройства используют Message Signaled Interrupts:

· Сетевые карты: 1 прерывание на пакет или на группу пакетов

· Диски: 1 прерывание на завершение I/O операции

4.4. Конкретные источники прерываний в данной системе

Диагностика (что нужно проверить):

# 1. Распределение прерываний по типам

cat /proc/interrupts | head -20

# Пример вывода:

#            CPU0       CPU1       ...

#   0:         41          0   IO-APIC   2-edge      timer

#   1:          0          0   IO-APIC   1-edge      i8042

#   8:          0          0   IO-APIC   8-edge      rtc0

#   9:          0          0   IO-APIC   9-fasteoi   acpi

#  16:   10345678    9876543   IO-APIC  16-fasteoi   ahci[0000:00:1f.2]  # Диск!

#  17:    8765432    9234567   IO-APIC  17-fasteoi   eno1                # Сеть!

# 2. Сетевые прерывания

cat /proc/interrupts | grep -E 'eth|eno|ens|em'

# 3. Прерывания по ядрам

mpstat -P ALL 1  # Посмотреть распределение прерываний по ядрам

Наиболее вероятные виновники данном случае:

1. Сетевая карта (50-70% прерываний):

  • PostgreSQL подключения (сотни/тысячи соединений)
  • Репликационный трафик
  • Мониторинговые запросы

2. Таймеры (20-40%):

  • CONFIG_HZ=250 или CONFIG_HZ=1000
  • Планировщик процессов
  • Таймеры ядра

3. Дисковые контроллеры (5-10%):

  • AHCI/SATA контроллер (vdb, vdc)
  • Возможно, RAID контроллер

4. Другие устройства:

  • Управление питанием (ACPI)
  • Serial console
  • Виртуализация (kvm)

4.5. Почему это критично для СУБД?

PostgreSQL-специфичные проблемы:

a) Процесс архивации WAL (archiver):

bash

# Если используется archive_command с scp/rsync

archive_command = 'scp %p standby:/wal_archive/%f'

# Каждый WAL-файл → сетевое соединение → прерывания

b) Репликация:

sql

-- Physical replication

primary: sends WAL segments via network

standby: receives and applies

-- Много сетевых прерываний на обеих сторонах

c) Автовакуум (autovacuum):

sql

-- Autovacuum сканирует таблицы, генерируя дисковые операции

-- Каждая страница → потенциальное прерывание

d) Очереди сообщений (listen/notify):

sql

LISTEN channel;

NOTIFY channel;

-- Каждое уведомление → сетевое взаимодействие

4.5. Почему это критично для СУБД?

PostgreSQL-специфичные проблемы:

a) Процесс архивации WAL (archiver):

# Если используется archive_command с scp/rsync

archive_command = 'scp %p standby:/wal_archive/%f'

# Каждый WAL-файл → сетевое соединение → прерывания

b) Репликация:

-- Physical replication

primary: sends WAL segments via network

standby: receives and applies

-- Много сетевых прерываний на обеих сторонах

c) Автовакуум (autovacuum):

-- Autovacuum сканирует таблицы, генерируя дисковые операции

-- Каждая страница → потенциальное прерывание

d) Очереди сообщений (listen/notify):

LISTEN channel;

NOTIFY channel;

-- Каждое уведомление → сетевое взаимодействие

4.6. Рекомендации по оптимизации

Немедленные действия:

a) Настройка прерываний (IRQ balancing):

# 1. Установка irqbalance

systemctl enable irqbalance

systemctl start irqbalance

# 2. Ручное распределение (если нужно)

# Привязать сетевые прерывания к определенным ядрам

echo 2-15 > /proc/irq/17/smp_affinity  # для IRQ 17

b) НастройкаCPU affinity для PostgreSQL:

bash

# Запускать PostgreSQL на ядрах 16-31

taskset -c 16-31 /usr/bin/postgres

# Или в systemd service:

[Service]

CPUSetCPUs=16-31

c) Увеличение частоты прерываний для сетевых карт:

bash

# Для eth0

ethtool -C eth0 rx-usecs 50  # Уменьшить задержку прерываний

ethtool -C eth0 rx-frames 32  # Группировать прерывания

d) Переход на polling режим (NAPI):

bash

# Для очень высоких нагрузок

ethtool -K eth0 ntuple on

Долгосрочные решения:

1. Использование RDMA/RoCE для репликации (уменьшает прерывания)

2. Переход на DPDK или XDP для сетевой обработки

3. Использование CPU с большим кэшем L3 (уменьшает cache miss после прерываний)

4. Оптимизация HZ ядра:

# В ядре CONFIG_HZ=100 (вместо 250/1000) для серверов

# Меньше таймерных прерываний

4.7. Заключение

Основной вывод:

Высокая корреляция cs-in (0.9439) указывает на системную проблему с прерываниями, которые доминируют в поведении системы. Даже при низкой утилизации CPU (us+sy <10%) это создает contention за кэш и ресурсы ядра, что критично для СУБД.

Наиболее вероятные источники:

1. Сетевые прерывания от клиентских соединений PostgreSQL и репликации

2. Таймерные прерывания от планировщика ядра

3. Дисковые прерывания от проблемного vdb

Влияние на СУБД: Увеличение latency транзакций на 30-50%, contention на внутренних блокировках, снижение эффективности кэша CPU, ограничение масштабируемости на многопроцессорных системах.

Рекомендация: Начать с анализа /proc/interrupts, настроить IRQ balancing и CPU affinity для изоляции ядер для PostgreSQL от обработчиков прерываний.

5. Сводная хронология событий и рекомендации

Контекст назначения дисков:

· /data на vdb - данные СУБД (табличные пространства)

· /wal на vdc - журнал транзакций WAL

Фаза 1: 15:25–15:40 - Начало аномалий

Первые сигналы (15:25-15:30):

Метрики, первыми показавшие аномалии:

1. Память (самый ранний сигнал):

memory_free = 1368 МБ (2.1% от 64 ГБ) - уже критично низко с первой минуты

memory_cache = 59.15 ГБ (92.4% RAM) - агрессивное кэширование

Вывод: Система начинала с уже перегруженной памятью.

2. Диск vdb (/data) - умеренный рост:

w/s: 14.52 → 16.22 (+11.7%)

wMB/s: 0.14 → 0.16 (+14.3%)

w_await: 1.88 мс (стабильно высокий)

3. Диск vdc (/wal) - более заметный рост:

w/s: 15.19 → 16.10 (+6%)

wMB/s: 0.15 → 0.16 (+6.7%)

Ключевое событие (15:30-15:40):

Рост операций записи:

vdb (данные): w/s стабилизировался на 16.22 операций/сек

vdc (WAL): w/s вырос до 18.75 операций/сек (+23.5% от начала)

Причина роста: Вероятно, начался интенсивный процесс в СУБД:

· Массовые UPDATE/INSERT

· Checkpoint

· Autovacuum больших таблиц

Фаза 2: 15:40–16:00 - Эскалация проблем

Стабилизация с высокими задержками:

1. vdb: w_await стабильно высокий:

15:40-15:50: 1.76-1.83 мс (выше порога 1 мс для HDD)

После 15:50: начинает снижаться до 1.48-1.70 мс

2. Память остается критически низкой:

memory_free: 1368-1382 МБ (2.1-2.2%)

memory_cache: продолжает расти до 59.16 ГБ

3. Прерывания достигают пика:

system_in: 17311 прерываний/сек (максимум в 15:40)

system_cs: 9552 переключений/сек (максимум в 15:40)

Важное наблюдение (15:58-16:00):

vdb w_await снижается до 1.39 мс, но...

w/s начинает снижаться с 16.22 до 14.57

Интерпретация: СУБД, возможно, начала ограничивать запись из-за накопленных проблем.

Фаза 3: 16:00–16:25 - Снижение нагрузки, сохранение проблем

Парадоксальная ситуация:

1. Нагрузка снижается, задержки остаются:

text

vdb w/s: 14.57 → 10.73 (-26.4%)

vdb w_await: 1.39 → 1.06 мс (улучшение, но все равно высоко)

2. Память начинает восстанавливаться (после 16:15):

text

16:15: memory_free = 2127 МБ

16:25: memory_free = 2794 МБ (+104% от минимума)

3. Прерывания снижаются:

text

system_in: 17311 → 15236 (-12%)

system_cs: 9552 → 8589 (-10%)

Хронология развития ситуации:

Время     | Событие

----------|--------------------------------------------------------

15:25      | Начало: память уже перегружена, диски работают с задержками

15:25-15:30| Рост операций записи на vdb и vdc

15:30-15:40| Пик нагрузки на запись, прерывания достигают максимума

15:40-16:00| Стабильно высокие задержки, память не восстанавливается

16:00-16:25| Нагрузка снижается, но задержки остаются, память медленно восстанавливается

Корневая цепочка причинно-следственных связей:

1. Первичная проблема: Агрессивное кэширование (memory_cache 59 ГБ) → недостаток свободной памяти для буферов записи

2. Вторичная проблема: Диск vdb (HDD для /data) не справляется с мелкими случайными операциями записи при недостатке RAM буферов

3. Третичная проблема: Высокие задержки записи на vdb → замедление COMMIT транзакций (т.к. WAL на vdc ждет данные с vdb)

4. Каскадный эффект: Накопление ожидающих транзакций → рост очереди → увеличение прерываний и переключений контекста

Рекомендации по настройке:

Оптимизация памяти и кэширования:

Немедленно (sysctl):

# Уменьшить максимальный процент грязных страниц

sysctl -w vm.dirty_ratio=10

sysctl -w vm.dirty_background_ratio=5

# Ускорить запись грязных страниц

sysctl -w vm.dirty_expire_centisecs=1000  # 10 секунд вместо 30

sysctl -w vm.dirty_writeback_centisecs=500  # 5 секунд

# Уменьшить swappiness для сервера БД

sysctl -w vm.swappiness=1

# Увеличить минимальный размер свободной памяти

sysctl -w vm.min_free_kbytes=1048576  # 1 ГБ

Для PostgreSQL:

# postgresql.conf

shared_buffers = 16GB           # 25% от 64GB RAM

work_mem = 8MB                  # Для уменьшения использования дисковых temp файлов

maintenance_work_mem = 1GB      # Для autovacuum и индексации

effective_cache_size = 48GB     # Помочь планировщику

wal_buffers = 16MB              # Увеличить буфер WAL

Настройка дисковой подсистемы:

Для vdb (/data - HDD предположительно):

# Переключить на deadline планировщик

echo deadline > /sys/block/vdb/queue/scheduler

# Увеличить глубину очереди

echo 1024 > /sys/block/vdb/queue/nr_requests

# Оптимизировать для СУБД

echo 128 > /sys/block/vdb/queue/read_ahead_kb

echo 0 > /sys/block/vdb/queue/rotational  # Установить 0, если это SSD

echo 1 > /sys/block/vdb/queue/rq_affinity

# Увеличить лимиты IO

echo 256 > /sys/block/vdb/queue/max_sectors_kb

Для vdc (/wal - должен быть самым быстрым диском):

# Для SSD использовать noop или none

echo noop > /sys/block/vdc/queue/scheduler

# Увеличить очередь для параллельной записи

echo 256 > /sys/block/vdc/queue/nr_requests

# Оптимизировать для последовательной записи

echo 512 > /sys/block/vdc/queue/read_ahead_kb

Оптимизации в PostgreSQL:

# postgresql.conf

wal_level = minimal             # Если не нужна репликация

synchronous_commit = off        # Для некоторых рабочих нагрузок

wal_writer_delay = 10ms         # Чаще писать WAL

wal_writer_flush_after = 1MB    # Больше накапливать перед записью

checkpoint_timeout = 10min      # Реже checkpoint

checkpoint_completion_target = 0.7  # Растянуть checkpoint

max_wal_size = 8GB              # Увеличить лимит WAL

min_wal_size = 2GB

Оптимизация прерываний CPU:

Настройка IRQ балансировки:

# Убедиться, что irqbalance работает оптимально

systemctl enable irqbalance

systemctl start irqbalance

# Конфигурация irqbalance (/etc/sysconfig/irqbalance)

IRQBALANCE_ARGS="--powerthresh=1 --idlethresh=2"

Ручное распределение прерываний:

# 1. Определить IRQ для дисков и сети

grep -E 'vdb|vdc|eth' /proc/interrupts

# 2. Привязать дисковые прерывания к отдельным ядрам (0-7)

echo ff > /proc/irq/[IRQ_NUMBER]/smp_affinity

# 3. Привязать сетевые прерывания к другим ядрам (8-15)

echo ff00 > /proc/irq/[NET_IRQ]/smp_affinity

# 4. Оставить ядра 16-31 для PostgreSQL

CPU affinity для PostgreSQL:

# В systemd service файле PostgreSQL

[Service]

CPUSetCPUs=16-31

CPUQuota=200%  # Ограничить, если нужно

Изоляция ядер:

# В параметрах ядра (GRUB)

isolcpus=16-31  # Изолировать ядра для PostgreSQL

nohz_full=16-31  # Отключить тики таймера

rcu_nocbs=16-31  # Вынести RCU на другие ядра

Оптимизация файловых систем:

# Для /data (vdb) - если это XFS

mount -o defaults,noatime,nodiratime,allocsize=1g,nobarrier /dev/vdb /data

# Для /wal (vdc) - если это ext4

mount -o defaults,noatime,nodiratime,data=writeback,barrier=0 /dev/vdc /wal

Резюме приоритетов:

1. Срочно: Оптимизировать параметры памяти (vm.dirty_*)

2. Срочно: Настроить планировщик дисков (deadline для vdb, noop для vdc)

3. Среднесрочно: Пересмотреть распределение данных (WAL на самый быстрый диск)

4. Долгосрочно: Апгрейд железа (SSD для /data, больше RAM)

Критический вывод:

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

Генерация отчета для администратора СУБД

6.Отчет об анализе инцидента производительности СУБД

Дата анализа: 16.12.2025

Период мониторинга: 15:25-16:25

Затронутые системы: PostgreSQL , Linux, 32 ядра, 64 ГБ RAM

Устройства хранения: vdb (/data), vdc (/wal)

6.1. КРАТКОЕ РЕЗЮМЕ

Основная причина инцидента: Каскадная деградация производительности, инициированная агрессивным кэшированием данных в ОС и усугубленная неоптимальным взаимодействием дисковой подсистемы с рабочими процессами СУБД.

Ключевой цепочка событий:

1. Первичный триггер: ОС использовала 92% RAM для кэширования данных (59/64 ГБ), оставляя недостаточно буферов для операций записи.

2. Критическое узкое место: Диск vdb ( /data) испытывал высокую задержку записи (w_await >5 мс в 62% случаев) при относительно низкой нагрузке.

3. Вторичные эффекты: Высокие задержки записи привели к contention на внутренних блокировках СУБД, увеличению времени фиксации транзакций и росту системных прерываний.

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

Корневая проблема: Несоответствие конфигурации дисковой подсистемы (/data) и политики кэширования ОС требованиям рабочей нагрузки СУБД.

6.2. ДЕТАЛИЗАЦИЯ: СВЯЗЬ МЕТРИК ИНФРАСТРУКТУРЫ И СИМПТОМОВ СУБД

6.2.1 Память → Проблемы с буферами и кэшем

-3

6.2.2 Дисковая подсистема → Замедление транзакций и checkpoint

-4

6.2.3 CPU и системные метрики → Contention и блокировки

-5

6.2.4 Хронология влияния на СУБД

15:25-15:40 (Рост нагрузки):

  • СУБД начинает интенсивные операции записи (массовые UPDATE/INSERT или checkpoint)
  • Память уже перегружена кэшем → shared_buffers конкурирует с page cache
  • WAL-записи на vdc ускоряются, но данные на vdb пишутся с задержкой

15:40-16:00 (Стагнация):

  • Транзакции накапливаются в очереди из-за медленной записи на vdb
  • Время фиксации (COMMIT) увеличивается с <1 мс до >5 мс
  • Автовакуум и другие фоновые процессы блокируются

16:00-16:25 (Восстановление):

  • СУБД начинает ограничивать новые операции (backpressure)
  • Нагрузка снижается, но задержки остаются из-за фрагментации и "горячих" блоков
  • Память медленно освобождается, но кэш все еще доминирует

6.3. РЕКОМЕНДАЦИИ

6.3.1 Немедленные действия (24 часа)

1. Диагностика диска vdb:

# Проверить состояние HDD

smartctl -a /dev/vdb | grep -E "Reallocated|Pending|Uncorrectable"

# Проверить планировщик ввода-вывода

cat /sys/block/vdb/queue/scheduler

echo deadline > /sys/block/vdb/queue/scheduler

# Увеличить глубину очереди

echo 1024 > /sys/block/vdb/queue/nr_requests

2. Оптимизация параметров памяти:

# Оптимизировать кэширование для рабочей нагрузки с записью

sysctl -w vm.dirty_ratio=10

sysctl -w vm.dirty_background_ratio=5

sysctl -w vm.dirty_expire_centisecs=1000

sysctl -w vm.swappiness=1

3. Экстренная настройка PostgreSQL:

# postgresql.conf (динамически изменяемые параметры)

ALTER SYSTEM SET checkpoint_timeout = '15min';

ALTER SYSTEM SET checkpoint_completion_target = 0.9;

ALTER SYSTEM SET wal_writer_delay = '50ms';

ALTER SYSTEM SET wal_writer_flush_after = '2MB';

ALTER SYSTEM SET effective_io_concurrency = 2;  # Для HDD

4. Мониторинг критических метрик:

-- Мониторинг в реальном времени

SELECT wait_event_type, wait_event, count(*)

FROM pg_stat_activity

WHERE wait_event IS NOT NULL

GROUP BY 1, 2 ORDER BY 3 DESC;

-- Проверка отставания репликации

SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replication_lag

FROM pg_stat_replication;

6.3.2 Среднесрочные действия (1-4 недели)

1. Реорганизация хранения данных:

ТЕКУЩАЯ КОНФИГУРАЦИЯ:

- vdb (медленный): /data  ← НЕПРАВИЛЬНО!

- vdc (предположительно SSD): /wal

ПРЕДЛАГАЕМАЯ КОНФИГУРАЦИЯ:

- vdc (SSD): /data           ← Данные и индексы

- vdb (HDD): /wal            ← WAL (если vdc действительно SSD)

- Если оба HDD: добавить SSD для WAL

2. Настройка параметров PostgreSQL для HDD:

# postgresql.conf

random_page_cost = 4.0        # Для HDD (вместо 1.1 для SSD)

effective_io_concurrency = 2  # Для HDD

seq_page_cost = 1.0

maintenance_work_mem = 2GB    # Для ускорения autovacuum

autovacuum_vacuum_cost_delay = 20ms

3. Настройка CPU affinity:

# Изолировать ядра для PostgreSQL

systemctl edit postgresql-13

# Добавить:

[Service]

CPUSetCPUs=16-31

CPUQuota=200%

Nice=-10

6.3.3 Долгосрочные решения (1-6 месяцев)

1. Апгрейд оборудования:

ВЫСОКИЙ ПРИОРИТЕТ:

1. SSD для /data (vdb заменить на SSD NVMe)

2. Увеличение RAM до 128-256 ГБ

3. Отдельный быстрый диск для WAL (если высоконагруженная OLTP)

СРЕДНИЙ ПРИОРИТЕТ:

1. RAID 10 для HDD (если переход на SSD невозможен)

2. Выделенные дисковые контроллеры

3. Оптимизация сети (10GbE, настройка interrupt coalescing)

2. Архитектурные изменения:

1. Внедрение connection pooling (PgBouncer)

2. Настройка горизонтального шардинга для больших таблиц

3. Выделение отдельного инстанса для отчетных запросов

4. Реализация read-only реплик для распределения нагрузки