СУБДОМИНАНТА — это музыка. Инфраструктура — концертный зал. Необхолимо найти трещинк в стене, из-за которой весь оркестр фальшивит.
Часть-1:СУБД.
Предисловие
В первой части исследования проведен анализ инцидента снижения производительности PostgreSQL на уровне СУБД. Были идентифицированы проблемные запросы и паттерны .
Однако, как показывает системный подход, деградация производительности сложных систем редко имеет единственную причину. Симптомы, наблюдаемые на уровне базы данных, часто являются следствием патологий в фундаментальных слоях инфраструктуры.
Данная статья представляет собой вторую, системную часть расследования. Её цель — перейти от наблюдаемых симптомов (медленные транзакции, блокировки) к установлению корневых причин на уровне операционной системы и аппаратного обеспечения.
Проведена декомпозицию инцидента, последовательно анализируя метрики дисковой подсистемы , управления памятью и обработки прерываний ЦПУ.
В итоге видно, как аномальная задержка записи на одном из накопителей (w_await >5 мс) при низкой утилизации, агрессивная политика кэширования, приведшая к дефициту свободной оперативной памяти, и лавина аппаратных прерываний образовали каскад взаимосвязанных проблем. Именно этот каскад и проявился для пользователей и приложений как критическое замедление работы СУБД.
Этот кейс служит практической иллюстрацией важности полностековой диагностики (Full-Stack Diagnostics), где глубокий анализ СУБД дополняется не менее тщательным исследованием среды её выполнения.
1.Сводный анализ узких мест инфраструктуры
1.1. Наиболее вероятное узкое место
Основная проблема: Диск vdb с высокой задержкой записи (w_await > 5мс)
Рейтинг проблем по критичности:
- Диск vdb - самый критичный компонент
- Недостаток памяти - вторичная проблема
- Высокая корреляция кэш-запись на vdc - симптоматичная, но менее критичная
- Высокая корреляция 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. Гипотеза влияния на СУБД
Сценарий развития инцидента:
- Фоновая проблема: Диск vdb имеет аппаратные или конфигурационные проблемы, приводящие к высокой задержке записи при относительно низкой нагрузке.
- Инициирующее событие: СУБД начинает интенсивные операции записи (например, checkpoint, массовые обновления, WAL-записи) на vdb.
- Каскадный эффект:
- Высокая задержка записи на vdb → замедление commit транзакций
- Процессы СУБД, ожидающие записи, блокируются или работают медленно
- Очередь запросов к СУБД растет
- Усугубляющие факторы:
- Память: Хотя свопинга нет, низкая свободная память может ограничивать буферы СУБД
- Кэш: Неэффективное использование кэша для vdc может указывать на неправильное распределение нагрузки между дисками
- CPU: Высокая корреляция cs-in (0.9439) может быть следствием проблем с диском - прерывания от устройств ввода-вывода вызывают переключения контекста
Конкретное влияние на СУБД:
- Замедление WAL (Write-Ahead Log): Если vdb используется для журнала транзакций, задержки записи приведут к накоплению незакоммиченных транзакций
- Blocking и deadlocks: Процессы, ожидающие записи, могут удерживать блокировки
- Checkpoint stalls: Если checkpoint не успевает записывать "грязные" страницы из-за медленного диска
- Connection pool exhaustion: Долгие транзакции занимают соединения, новые запросы не могут получить соединение
1.4. Рекомендации для немедленного анализа
Проверить:
- Диск vdb:
- SMART-статус диска
- Параметры монтирования (noatime, nodiratime, barrier)
- Настройки планировщика ввода-вывода
- Конкуренция за ресурсы с другими дисками на том же контроллере
- Распределение данных СУБД:
- Что хранится на vdb vs vdc? (Возможно, WAL на vdb, данные на vdc)
- Соответствие нагрузки характеристикам дисков
- Настройки памяти:
- Значение vm.swappiness
- Параметры кэширования (vm.dirty_ratio, vm.dirty_background_ratio)
- Настройки 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 показывают:
Расчет: При 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 Память → Проблемы с буферами и кэшем
6.2.2 Дисковая подсистема → Замедление транзакций и checkpoint
6.2.3 CPU и системные метрики → Contention и блокировки
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 реплик для распределения нагрузки