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

PG_EXPECTO : checkpoint_timeout = '10m'.

GitHub - Комплекс pg_expecto для статистического анализа производительности и нагрузочного тестирования СУБД PostgreSQL Проанализировать метрики производительности СУБД и инфраструктуры в ходе нагрузочного тестирования при значении параметра checkpoint_timeout = 10 минут. Важная деталь по графикам Периодические провалы операционной скорости это не влияние checkpoint , это длительное выполнение конкурентных update в сценарии-6, в то время как остальные сценарии завершили работу. Ожидания СУБД типа IO Ожидания СУБД типа LWLock Вывод: Нагрузка увеличивалась ступенчато с резкими скачками, что позволяет оценить поведение системы на разных уровнях нагрузки. Ключевые паттерны: Корреляция SPEED-WAITINGS: Коэффициенты корреляции с WAITINGS: Важные наблюдения: SPEED : WAITINGS : Система демонстрирует классическую картину contention на блокировках. Основная проблема - один запрос (scenario6) создает каскад блокировок, что при увеличении нагрузки приводит к экспоненциальному росту ожиданий и паден
Оглавление
checkpoint_timeout = '10m'
checkpoint_timeout = '10m'

GitHub - Комплекс pg_expecto для статистического анализа производительности и нагрузочного тестирования СУБД PostgreSQL

Подготовка эксперимента.

Задача эксперимента

Проанализировать метрики производительности СУБД и инфраструктуры в ходе нагрузочного тестирования при значении параметра checkpoint_timeout = 10 минут.

Исходные данные

Регрессионный и корреляционный анализ

-2

Операционная скорость СУБД

График изменения операционной скорости в ходе нагрузочного тестирования.
График изменения операционной скорости в ходе нагрузочного тестирования.

Важная деталь по графикам

Периодические провалы операционной скорости это не влияние checkpoint , это длительное выполнение конкурентных update в сценарии-6, в то время как остальные сценарии завершили работу.

Ожидания СУБД

График изменения ожиданий СУБД в ходе нагрузочного тестирования
График изменения ожиданий СУБД в ходе нагрузочного тестирования

Ожидания СУБД типа IO

График изменения ожиданий типа IO в ходе нагрузочного тестирования.
График изменения ожиданий типа IO в ходе нагрузочного тестирования.

Ожидания СУБД типа LWLock

График изменения ожиданий типа LWLock в ходе нагрузочного тестирования
График изменения ожиданий типа LWLock в ходе нагрузочного тестирования

Типы ожиданий СУБД (Диаграмма Парето)

-7

События ожиданий по тестовым запросам (Диаграмма Парето)

-8

1.Анализ производительности PostgreSQL по результатам нагрузочного тестирования

1.1. Динамика изменения нагрузки (LOAD)

  • Нагрузка плавно нарастала от 7 до 22 в течение 2 часов 45 минут (12:51-15:36)
  • Основные этапы роста:
    12:51-13:08: стабильная нагрузка 7
    13:09-14:02: плавный рост 8 → 10
    14:21-14:39: скачок до 12
    14:40-14:57: рост до 13
    14:58-15:08: скачок до 15
    15:09-15:25: скачок до 18
    15:26-15:36: пиковая нагрузка 22

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

1.2. Изменение операционной скорости(SPEED) и ожиданий (WAITINGS)

Ключевые паттерны:

  • SPEED :
    Начальные значения: ~314 (очень низкие при старте)
    Пиковые значения: ~1.9 млн (12:52-13:00)
    Постепенное снижение до ~678,012 к концу теста
    Резкие падения до значений ~314-868 в моменты изменения нагрузки
  • WAITINGS :
    Начальные значения: ~3,605-4,856
    Постепенный рост до 8,940 к концу теста
    Увеличение на 148% за время тестирования

Корреляция SPEED-WAITINGS:

  • Коэффициент корреляции: -0.47 (умеренная отрицательная)
  • Интерпретация: С ростом ожиданий производительность снижается, но не линейно

1.3. Анализ отдельных типов ожиданий

Коэффициенты корреляции с WAITINGS:

  1. LOCK (блокировки): +0.82 (сильная положительная)
  2. LWLOCK (легковесные блокировки): +0.83 (сильная положительная)
  3. IO (ввод-вывод): -0.72 (сильная отрицательная)
  4. TIMEOUT (таймауты): -0.39 (умеренная отрицательная)

Важные наблюдения:

  • LOCK и LWLOCK - основные драйверы роста WAITINGS
  • IO имеет отрицательную корреляцию, что необычно. Возможное объяснение: когда система упирается в блокировки, процессы не успевают генерировать IO-нагрузку

1.4. Статистический анализ регрессии

SPEED :

  • R² = 0.31 - только 31% вариаций SPEED объясняются временем/нагрузкой
  • Угол наклона = -28.95 - четкая тенденция к снижению производительности со временем

WAITINGS :

  • R² = 0.52 - 52% вариаций WAITINGS объясняются временем/нагрузкой
  • Угол наклона = +35.77 - четкая тенденция к росту ожиданий со временем

Основные узкие места

1. Блокировки (LOCK и LWLOCK) - главная проблема

  • Составляют 80% всех ожиданий (принцип Парето)
  • LockManager в LWLOCK: 82.35% всех LWLock-ожиданий
  • transactionid в LOCK: 89.88% всех Lock-ожиданий
  • QueryID -4261790647437368643 (scenario6) вызывает:
    100% всех Lock-ожиданий
    95.25% всех LWLock-ожиданий

2. Влияние нагрузки на производительность

  • Паттерн: С каждым скачком нагрузки происходит:
    Временное падение производительности
    Рост ожиданий
    Частичное восстановление, но до более низкого уровня
  • Система не масштабируется линейно: при росте нагрузки с 7 до 22 (в 3.1 раза) производительность упала примерно в 2.8 раза

3. Признаки contention (конкуренции)

  • Явные признаки: Высокие коэффициенты корреляции LOCK/LWLOCK с WAITINGS
  • Типы contention:
    Блокировки транзакций (transactionid)
    - вероятно, длительные транзакции или deadlock
    Конкуренция за менеджер блокировок (LockManager) - возможно, слишком много одновременных транзакций
  • Подтверждение из метрик VMSTAT: Высокая нагрузка CPU (us+sy = 75.9%), процессы в run queue превышают ядра CPU в 89.76% наблюдений

4. Дополнительные проблемы

  • Память: Свободная RAM < 5% в 85.54% наблюдений
  • Производительность дисков: В норме (загрузка < 50%, отклик < 5мс)
  • CPU перегрузка: Очередь процессов (procs_r) превышает 8 ядер в 89.76% наблюдений

Рекомендации для первоочередного анализа:

  1. Исследовать scenario6 - главный источник блокировок
  2. Проанализировать распределение блокировок - проверить long-running transactions
  3. Оптимизировать настройки блокировок - возможно, уменьшить max_connections с 1000
  4. Добавить мониторинг contention - отслеживать wait events в реальном времени
  5. Рассмотреть увеличение RAM - учитывая постоянную нехватку памяти

Вывод:

Система демонстрирует классическую картину contention на блокировках. Основная проблема - один запрос (scenario6) создает каскад блокировок, что при увеличении нагрузки приводит к экспоненциальному росту ожиданий и падению производительности.

2. Анализ ожиданий по типам (Wait Events) и SQL-запросам

2.1. Типы ожиданий, составляющие 80% от всех ожиданий

Анализ по принципу Парето:

Lock (тяжелые блокировки):

  • transactionid: 344,442 события (89.88% всех Lock-ожиданий)
  • tuple: также присутствует в списке для queryid -4261790647437368643

LWLock (легковесные блокировки):

  • LockManager: 11,092 события (82.35% всех LWLock-ожиданий)
  • Другие типы LWLock (BufferContent, BufferMapping, pg_stat_statements, ProcArray, XidGen): в сумме менее 20%

Вывод: Для достижения 80% покрытия:

  • Lock: достаточно только transactionid (89.88%)
  • LWLock: достаточно только LockManager (82.35%)

2.2. Анализ SQL-запросов, вызывающих наибольшее количество ожиданий

Список запросов:

-9191196513623730485 | select scenario5()
-1247703524680257765 | select scenario2()
-2326249669894930556 | select scenario3()
3162304388436908605 | select scenario4()
-4261790647437368643 | select scenario6() ← ГЛАВНЫЙ ВИНОВНИК
-3890986969840966328 | select scenario1()

Статистика по queryid -4261790647437368643 (scenario6):

  • CALLS: 129,514 выполнений за период теста
  • Wait Events по Lock: 383,243 события (100% всех Lock-ожиданий)
  • Wait Events по LWLock: 12,829 событий (95.25% всех LWLock-ожиданий)
  • DBNAME/ROLE: demo/postgres

Остальные сценарии (1-5):

  • Не показывают значимых ожиданий Lock/LWLock в отчете
  • Вероятно, это запросы чтения или менее конфликтные операции

2.3. Почему scenario6 вызывает столько ожиданий?

Анализ wait event list для scenario6:

Lock события:

  1. transactionid - ожидание завершения транзакций
  2. tuple - блокировки на уровне строк

LWLock события:

  1. BufferContent - конкуренция за доступ к буферам данных
  2. BufferMapping - конкуренция за маппинг буферов
  3. LockManager - конкуренция за менеджер блокировок (наиболее частый!)
  4. pg_stat_statements - блокировки при записи статистики
  5. ProcArray - конкуренция за доступ к массиву процессов
  6. XidGen - конкуренция за генерацию ID транзакций

Гипотезы о природе scenario6:

  1. Тяжелая запись/обновление - множественные операции UPDATE/DELETE
  2. Длительные транзакции - удерживающие блокировки продолжительное время
  3. Высокая конкуренция за строки - много процессов пытаются изменить одни и те же данные
  4. Частые коммиты - создающие нагрузку на LockManager и XidGen

Дополнительные улики:

  • 129,514 выполнений за ~3 часа = ~12 выполнений в секунду в среднем
  • Высокая нагрузка на ProcArray указывает на много конкурентных процессов
  • BufferContent/BufferMapping указывают на активную работу с данными

2.4. Рекомендации по оптимизации запросов и настройке СУБД

А. Оптимизация scenario6:

  1. Анализ плана выполнения:textEXPLAIN (ANALYZE, BUFFERS, VERBOSE) select scenario6();
  2. Возможные оптимизации запроса:
    Разделить на более мелкие транзакции
    Использовать FOR UPDATE SKIP LOCKED для избежания блокировок ожидания
    Переписать с использованием CTE или временных таблиц для уменьшения времени блокировок
    Добавить индексы для уменьшения времени поиска строк
  3. Изоляция транзакций:
    Проверить возможность использования READ COMMITTED с row-level locks
    Рассмотреть REPEATABLE READ или SERIALIZABLE если требуется строгая консистенция

Б. Настройка PostgreSQL для снижения блокировок:

1.Параметры блокировок:

-- Увеличить максимальное количество блокировок
ALTER SYSTEM SET max_locks_per_transaction = 512;
-- с текущих 256

-- Увеличить максимальное количество предикатных блокировок
ALTER SYSTEM SET max_pred_locks_per_transaction = 512;
-- с текущих 256

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

-- Уменьшить количество одновременных подключений
ALTER SYSTEM SET max_connections = 500;
-- с текущих 1000

-- Увеличить количество процессов автовакуума
ALTER SYSTEM SET autovacuum_max_workers = 8;
-- с текущих 4

-- Ускорить автовакуум
ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 2000;
-- с текущих 4000
ALTER SYSTEM SET autovacuum_vacuum_cost_delay = '1ms';
-- с текущих 2ms

3.Оптимизация памяти для снижения конкуренции:

-- Увеличить shared_buffers для кэширования блоков
ALTER SYSTEM SET shared_buffers = '4GB';
-- с текущих 2GB

-- Увеличить work_mem для операций в памяти
ALTER SYSTEM SET work_mem = '64MB';
-- с текущих 32MB

-- Увеличить maintenance_work_mem для автовакуума
ALTER SYSTEM SET maintenance_work_mem = '1GB';
-- с текущих 512MB

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

  1. Partitioning - разделить горячие таблицы на партиции
  2. Sharding - распределить данные по нескольким серверам
  3. Read Replicas - перенести чтение на реплики
  4. Connection Pooling - использовать пул соединений (например, PgBouncer)
  5. Queue-based Processing - использовать очереди (например, RabbitMQ) для асинхронной обработки

Г. Рекомендации по разработке:

  1. Retry Logic - реализовать повторные попытки при deadlock
  2. Exponential Backoff - использовать экспоненциальную задержку между повторами
  3. Smaller Transactions - разбивать большие транзакции на мелкие
  4. Lock Timeout - устанавливать разумные таймауты блокировокsqlSET lock_timeout = '5s';

Критический путь оптимизации:

  1. Сначала - проанализировать и оптимизировать scenario6
  2. Затем - настроить параметры PostgreSQL для снижения конкуренции
  3. Параллельно - мониторить и настраивать инфраструктуру
  4. В долгосрочной перспективе - рассмотреть архитектурные изменения

Показатели успеха:

  • Снижение LockManager wait events на 50%+
  • Уменьшение transactionid wait events на 70%+
  • Стабилизация производительности при пиковой нагрузке
  • Уменьшение времени отклика scenario6

3: Анализ метрик инфраструктуры (vmstat, iostat)

3.1. Анализ CPU

Процессы в run queue (procs_r) превышают количество ядер в 89,76% наблюдений

Данные:

  • CPU: 8 ядер
  • procs_r (процессы в run queue):
    MIN: 4 процесса
    MAX: 56 процессов
    В 89,76% наблюдений превышает 8 процессов

Причины:

  1. Высокий contention блокировок в PostgreSQL:
    Процессы готовы к выполнению, но ожидают освобождения блокировок
    Активное переключение контекста между процессами, ожидающими одних ресурсов
  2. Настройки PostgreSQL:
    max_connections = 1000 - слишком высокое значение для 8 ядер
    max_parallel_workers = 16 - может создавать излишнюю конкуренцию
    max_worker_processes = 16 - дополнительная нагрузка на планировщик
  3. Характер нагрузки:
    Большое количество конкурентных транзакций (особенно scenario6)
    Частые переключения контекста из-за блокировок

Высокая корреляция переключений контекста (cs) с system time (sy) и user time (us)

Коэффициенты корреляции:

  • cs-sy: 0.9653 (очень высокая)
  • cs-us: 0.8449 (высокая)
  • cs-in: 0.6647 (средняя)

Интерпретация:

  1. cs-sy (0.9653):
    Почти идеальная линейная зависимость
    Ядро тратит огромное время на переключение контекста вместо полезной работы
    Причина: Слишком много процессов конкурируют за CPU из-за блокировок
  2. cs-us (0.8449):
    Высокая корреляция с пользовательским временем
    Пользовательские процессы часто прерываются и возобновляются
    Причина: Активное перепланирование из-за блокировок
  3. Статистика переключений контекста:
    MIN: 692 переключений/сек
    MAX: 33,912 переключений/сек (очень высокое значение)
    В среднем: ~15-20 тыс. переключений/сек при нагрузке

Признаки нехватки CPU:

  1. Косвенные признаки:
    Очередь процессов постоянно превышает число ядер
    Высокое user time (us до 95%)
    Очень высокие переключения контекста
  2. Прямые признаки:
    cpu_us + cpu_sy = 75.9% наблюдений превышает 80% (ALARM в отчете)
    procs_r превышает ядра в 89.76% наблюдений
    Низкий cpu_id (простой CPU): часто 0-2%
  3. Вывод: CPU является вторичным узким местом, вызванным первичной проблемой - блокировками в PostgreSQL

3.2. Анализ RAM

Свободная память менее 5% в 85,54% наблюдений

Данные:

  • Общий RAM: 7.5 GB
  • memory_free:
    MIN: 238 MB (3.1% от 7.5 GB)
    MAX: 571 MB (7.6%)
    В 85.54% наблюдений < 5% свободно

Причины:

  1. PostgreSQL shared_buffers = 2GB - занимает значительную часть памяти
  2. Системный кэш активно используется:
    memory_cache: 6.25-6.61 GB (основной потребитель)
    memory_buff: 478-497 MB
  3. Рабочая нагрузка интенсивно использует кэширование

Оценка ситуации:

  • Не критично: Система активно использует память для кэширования
  • Опасно: При резком увеличении нагрузки может начаться свопинг
  • Эффективно: Отсутствие свопинга указывает на хорошую настройку памяти

Признаки свопинга (si, so)

Данные:

  • swap_si (swap in): 0 во всех наблюдениях
  • swap_so (swap out): 0 во всех наблюдениях
  • memory_swpd (используемый swap): 58 MB (константа, минимальное значение)

Вывод: Свопинг не используется, что хорошо:

  1. Память настроена адекватно рабочей нагрузке
  2. PostgreSQL не вытесняет страницы на диск
  3. Система работает в рамках доступной памяти

3.3. Анализ IO

Загрузка дисков (vdc и vdd)

vdc (/wal - WAL логи):

  • %util: 0.03% (минимальная загрузка)
  • w/s: 0.37 операций записи/сек
  • wMB/s: 0 MB/сек
  • w_await: 0.95-1.63 мс (отличное время отклика)

vdd (/data - данные):

  • %util: 0.02% (минимальная загрузка)
  • w/s: 0.32 операций записи/сек
  • wMB/s: 0 MB/сек
  • w_await: 1.63-49 мс (хорошее время отклика)

Вывод: Дисковая подсистема практически не нагружена

Корреляция между IO-ожиданиями в СУБД и системными метриками IO

Коэффициенты корреляции:

  • IO-wa: 0.0000 (отсутствует)
  • IO-b: 0.0000 (отсутствует)
  • IO-bi: 0.0000 (отсутствует)
  • IO-util (vdc/vdd): 0.0000 (отсутствует)

Интерпретация:

  1. Несоответствие: В СУБД есть IO ожидания, но системные метрики показывают нулевую загрузку дисков
  2. Возможные объяснения:
    Ожидания связаны с логическим IO (буферы в памяти), а не физическим
    Конкуренция за доступ к буферному кэшу
    Ожидания на уровне файловой системы, а не диска

Признаки проблем с дисковой подсистемой

Отсутствуют:

  • %util < 1% (при проблемах > 50-70%)
  • await < 5 мс (при проблемах > 10-20 мс)
  • Очереди отсутствуют (aqu_sz = 0)
  • Нет чтения (r/s = 0), только минимальная запись

Вывод: Дисковая подсистема не является узким местом

3.4. Общие выводы и связь с ожиданиями СУБД

Иерархия узких мест:

  1. Первичное узкое место: Блокировки в PostgreSQL (LOCK/LWLOCK)
    Создают каскадный эффект
    Приводят к росту очереди процессов
  2. Вторичное узкое место: CPU
    Вызвано переключениями контекста из-за блокировок
    Очередь процессов > числа ядер
  3. Третичное узкое место: Память
    Минимальный свободный объем, но без свопинга
    Потенциальный риск при росте нагрузки
  4. Не является узким местом: IO
    Диски практически простаивают
    Отличные времена отклика

Связь системных метрик с ожиданиями СУБД:

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

scenario6 (частые транзакции)

Высокий contention блокировок (transactionid, LockManager)

Процессы ожидают в run queue (procs_r ↑)

Частые переключения контекста (cs ↑)

Рост system time (sy ↑) за счет планировщика

Снижение полезной работы CPU (us ↓ относительно)

Общее падение производительности (SPEED ↓)

Подтверждение из метрик:

  1. procs_r до 56 при 8 ядрах → процессы блокированы
  2. cs до 33,912/сек → активное перепланирование
  3. cpu_sy коррелирует с cs (0.9653) → ядро занято переключениями
  4. Отсутствие IO нагрузки → проблема не в дисках
  5. Минимальная свободная память → система использует кэши, но не свопит

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

  1. CPU (высокий приоритет):
    Увеличить количество ядер (16+)
    Рассмотреть CPU с большим кэшем L3
    Настроить CPU affinity для процессов PostgreSQL
  2. Память (средний приоритет):
    Увеличить до 16-32 GB
    Позволит увеличить shared_buffers и кэши
    Снизит риск свопинга при пиках
  3. Настройка ОС:# Увеличить лимиты для PostgreSQL
    echo "postgres soft nofile 65536" >> /etc/security/limits.conf
    echo "postgres hard nofile 65536" >> /etc/security/limits.conf

    # Настройка swappiness (уже низкая, судя по отсутствию свопинга)
    echo "vm.swappiness = 1" >> /etc/sysctl.conf

    # Настройка dirty pages
    echo "vm.dirty_background_ratio = 5" >> /etc/sysctl.conf
    echo "vm.dirty_ratio = 10" >> /etc/sysctl.conf
  4. Мониторинг:
    Внедрить alert на procs_r > 2×ядер
    Мониторить cs > 10,000/сек
    Отслеживать рост memory_swpd

Ключевой вывод:

Проблема не в инфраструктуре, а в конфигурации и использовании PostgreSQL. Инфраструктурные улучшения (особенно CPU) помогут смягчить симптомы, но не устранят коренную причину - contention блокировок в scenario6.

4. Сводный анализ и рекомендации

4.1. Основная проблема нагрузочного тестирования

Первичное ограничение производительности: CONTENTION БЛОКИРОВОК

Узкое место: Один запрос (scenario6, queryid -4261790647437368643) создает каскад блокировок, который при увеличении нагрузки вызывает эффект "лавины":

scenario6 (интенсивные транзакции)

Высокий contention на transactionid и LockManager

Процессы ожидают в run queue (procs_r до 56 при 8 ядрах)

Экспоненциальный рост переключений контекста (cs до 33,912/сек)

Ядро тратит время на планирование (sy коррелирует с cs: 0.9653)

Падение полезной работы CPU и производительности (SPEED ↓)

Вторичные факторы:

  • CPU становится узким местом из-за переключений контекста
  • Память используется под завязку (хотя свопинг отсутствует)

4.2. Рекомендации по настройке PostgreSQL

А. Параметры блокировок (locks, lwlocks):

-- Текущие значения: max_locks_per_transaction = 256
-- Рекомендуемые:
ALTER SYSTEM SET max_locks_per_transaction = 512;
ALTER SYSTEM SET max_pred_locks_per_transaction = 512;

-- Уменьшение времени ожидания блокировок
ALTER SYSTEM SET deadlock_timeout = '2s';
-- вместо 1s по умолчанию

-- Увеличение размера таблицы блокировок
ALTER SYSTEM SET max_connections = 500;
-- с 1000, слишком много для 8 ядер

-- Оптимизация легковесных блокировок
ALTER SYSTEM SET max_worker_processes = 8;
-- с 16, уменьшить конкуренцию

Б. Настройки памяти:

-- Увеличение shared_buffers (но осторожно с 7.5 GB RAM)
ALTER SYSTEM SET shared_buffers = '3GB';
-- с 2GB, но требует увеличения RAM

-- Увеличение work_mem для сложных операций
ALTER SYSTEM SET work_mem = '64MB';
-- с 32MB

-- Увеличение maintenance_work_mem для автовакуума
ALTER SYSTEM SET maintenance_work_mem = '1GB';
-- с 512MB

-- Оптимизация кэширования планов
ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements, pg_wait_sampling, pg_prewarm';

-- Настройка памяти для сортировки и хеширования
ALTER SYSTEM SET hash_mem_multiplier = 1.5;
-- с 2.0, уменьшить при нехватке RAM

В. Параметры параллелизации и autovacuum:

-- Оптимизация параллельных запросов
ALTER SYSTEM SET max_parallel_workers_per_gather = 2;
-- с 1
ALTER SYSTEM SET max_parallel_workers = 8;
-- с 16, уменьшить до количества ядер
ALTER SYSTEM SET parallel_setup_cost = 10;
ALTER SYSTEM SET parallel_tuple_cost = 0.01;

-- Агрессивный autovacuum для борьбы с bloat
ALTER SYSTEM SET autovacuum_max_workers = 6;
-- с 4
ALTER SYSTEM SET autovacuum_vacuum_cost_delay = '1ms';
-- с 2ms
ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 2000;
-- с 4000, но чаще
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.05;
-- с 0.01, реже
ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02;
-- с 0.005

-- Ускорение контрольных точек
ALTER SYSTEM SET checkpoint_completion_target = 0.7;
-- с 0.9, быстрее освобождать WAL
ALTER SYSTEM SET wal_buffers = '16MB';
-- с auto (обычно 16MB)

4.3. Оптимизация SQL-запросов (особенно scenario6)

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

-- 1. Анализ плана выполнения
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, TIMING)
SELECT scenario6();

-- 2. Проверка на long-running transactions
SELECT pid, now() - xact_start AS duration, query, wait_event_type, wait_event
FROM pg_stat_activity
WHERE query LIKE '%scenario6%'
ORDER BY duration DESC;

-- 3. Разделение на более мелкие транзакции
-- Вместо одной большой транзакции:
BEGIN;
-- Много операций
COMMIT;

-- Использовать пакетную обработку:
DO $$
DECLARE
batch_size INT := 1000;
BEGIN
FOR i IN 0..(total_rows/batch_size) LOOP
BEGIN
-- Операции с batch_size строками
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE NOTICE 'Batch % failed: %', i, SQLERRM;
END;
END LOOP;
END $$;

Б. Оптимизационные техники:

-- 1. Использование SKIP LOCKED для конкурентного доступа
SELECT * FROM table WHERE condition
FOR UPDATE SKIP LOCKED
LIMIT 100;

-- 2. Оптимистичные блокировки
UPDATE table
SET column = value, version = version + 1
WHERE id = target_id AND version = current_version;

-- 3. Уменьшение времени удержания блокировок
-- Вынести сложные вычисления за пределы транзакции
BEGIN;
-- Только операции изменения данных
COMMIT;

-- 4. Индексы для уменьшения времени поиска
-- Проверить, есть ли индексы на колонках в WHERE/JOIN
CREATE INDEX CONCURRENTLY idx_table_column ON table(column)
WHERE condition;

-- 5. Материализованные представления для тяжелых агрегаций
CREATE MATERIALIZED VIEW mv_heavy_aggregation AS
SELECT ...
-- тяжелый запрос
WITH DATA;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_heavy_aggregation;

В. Мониторинг и профилирование scenario6:

-- Включение расширенного мониторинга
ALTER SYSTEM SET track_io_timing = on;
ALTER SYSTEM SET track_functions = all;
ALTER SYSTEM SET pg_stat_statements.track = all;

-- Запросы для анализа
WITH top_queries AS (
SELECT queryid, query, calls, total_exec_time,
mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10
)
SELECT * FROM top_queries;

4.4. Рекомендации по инфраструктуре

А. CPU - ВЫСОКИЙ ПРИОРИТЕТ:

Текущее состояние: 8 ядер, перегружены (procs_r до 56, cs до 33,912/сек)

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

  1. Увеличить до 16 ядер - это снизит нагрузку на планировщик
  2. Выбрать CPU с большим кэшем L3 (512KB L1, 32MB L2, 128MB L3 сейчас)
  3. Настроить CPU affinity/pinning:# Привязка PostgreSQL к определенным ядрам
    taskset -cp 0-7,16-23 $(head -1 /var/lib/postgresql/*/postmaster.pid)
  4. Включить CPU governor performance mode:bashcpupower frequency-set -g performance

Б. RAM - СРЕДНИЙ ПРИОРИТЕТ:

Текущее состояние: 7.5 GB, свободно <5% в 85.54% наблюдений

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

  1. Увеличить до 16-32 GB - позволит:
    Увеличить shared_buffers до 4-8GB
    Увеличить системный кэш
    Исключить риск свопинга
  2. Настройка параметров ядра:bash# Уменьшение swappiness (уже хорошо)
    echo "vm.swappiness = 1" >> /etc/sysctl.conf

    # Увеличение dirty pages limits
    echo "vm.dirty_background_bytes = 67108864" >> /etc/sysctl.conf
    # 64MB
    echo "vm.dirty_bytes = 1073741824" >> /etc/sysctl.conf
    # 1GB

    # Настройка огромных страниц
    echo "vm.nr_hugepages = 1024" >> /etc/sysctl.conf

В. IO-подсистема - НИЗКИЙ ПРИОРИТЕТ:

Текущее состояние: Диски не нагружены (%util < 1%, await < 5мс)

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

  1. Текущая конфигурация адекватна
  2. Рассмотреть SSD/NVMe для WAL (vdc) - хотя сейчас не требуется
  3. Настройка планировщика IO:# Для SSD
    echo deadline > /sys/block/vdc/queue/scheduler
    echo 256 > /sys/block/vdc/queue/nr_requests

4.5. Улучшение поведения системы под нагрузкой

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

1. Connection pooling:

yaml

# PgBouncer конфигурация
[databases]
demo = host=localhost dbname=demo

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 100
reserve_pool_size = 20

2. Read replicas для распределения нагрузки:

-- Настройка репликации
ALTER SYSTEM SET wal_level = logical;
ALTER SYSTEM SET max_replication_slots = 10;
ALTER SYSTEM SET max_wal_senders = 10;

3. Шардирование/партиционирование горячих таблиц:

CREATE TABLE orders (
id bigserial,
created_at timestamp,
customer_id int
) PARTITION BY RANGE (created_at);

CREATE TABLE orders_2024_q1 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');

5. Детальный анализ корреляций

5.1. Наиболее сильные корреляции

Положительные корреляции:

  1. WAITINGS - LWLOCK: 0.83 (очень сильная)
  2. WAITINGS - LOCK: 0.82 (очень сильная)
  3. cs - sy: 0.9653 (почти идеальная) - из vmstat
  4. cs - us: 0.8449 (очень сильная) - из vmstat
  5. cs - in: 0.6647 (сильная) - из vmstat

Отрицательные корреляции:

  1. WAITINGS - IO: -0.72 (сильная отрицательная)
  2. SPEED - WAITINGS: -0.47 (умеренная отрицательная)
  3. WAITINGS - TIMEOUT: -0.39 (умеренная отрицательная)

Нулевые корреляции:

  1. WAITINGS - BUFFERPIN: 0.0000
  2. WAITINGS - EXTENSION: 0.0000
  3. WAITINGS - IPC: 0.0000
  4. IO-wa: 0.0000 - из vmstat/iostat

5.2. Интерпретация корреляций WAITINGS с LOCK (0.82) и LWLOCK (0.83)

LOCK (0.82):

  • Высокая положительная корреляция означает, что рост общих ожиданий на 82% объясняется ростом ожиданий на тяжелые блокировки
  • Конкретно: Основной тип - transactionid (89.88% всех Lock ожиданий)
  • Интерпретация: Система страдает от конкуренции за транзакционные блокировки. Процессы долго ждут завершения других транзакций

LWLOCK (0.83):

  • Еще более высокая корреляция - легковесные блокировки еще сильнее влияют на общие ожидания
  • Конкретно: Основной тип - LockManager (82.35% всех LWLock ожиданий)
  • Интерпретация: Конкуренция за менеджер блокировок PostgreSQL. Когда много процессов одновременно запрашивают блокировки, менеджер становится узким местом

Совместная интерпретация:

Высокие транзакционные блокировки (LOCK)

Частые запросы на получение/освобождение блокировок

Перегрузка менеджера блокировок (LWLOCK)

Экспоненциальный рост общих ожиданий (WAITINGS)

5.3. Интерпретация отрицательных корреляций

WAITINGS - IO (-0.72):

Парадокс: При росте общих ожиданий, IO-ожидания снижаются.

Объяснения:

  1. Эффект блокировки: Процессы не могут прогрессировать из-за блокировок, поэтому не доходят до операций IOtextПроцесс блокирован → Не выполняет работу → Не генерирует IO
  2. Кэширование в памяти: Данные уже в буферном кэше, но доступ блокированtextДанные в памяти (IO не требуется) → Но заблокированы → Ожидания растут, IO нет
  3. Подтверждение из системных метрик:
    io_bi (blocks in) = 0 во всех наблюдениях
    io_bo (blocks out) = 94-110 (минимальные значения)
    Диски практически не используются (%util < 1%)

WAITINGS - TIMEOUT (-0.39):

Менее сильная, но значимая отрицательная корреляция.

Объяснение:

  • Таймауты возникают при ожидании внешних ресурсов или при сетевых операциях
  • В данной системе основной проблемой являются внутренние блокировки, а не внешние ресурсы
  • Когда процессы блокированы на внутренних ресурсах, они не успевают достичь точек, где могли бы возникнуть таймауты

5.4. Как корреляции помогают понять природу узких мест?

Диагностика по корреляционной матрице:

  1. Исключение ложных причин:
    WAITINGS-IO = -0.72 → Исключаем диски как узкое место
    WAITINGS-BUFFERPIN = 0.00 →
    Исключаем конкуренцию за буферы
    WAITINGS-IPC = 0.00 → Исключаем проблемы межпроцессного взаимодействия
  2. Подтверждение реальных причин:
    WAITINGS-LOCK = 0.82 → Подтверждаем блокировки транзакций
    WAITINGS-LWLOCK = 0.83 → Подтверждаем конкуренцию за менеджер блокировок
    SPEED-WAITINGS = -0.47 → Подтверждаем, что ожидания снижают производительность
  3. Построение цепочки причинно-следственных связей:

Первичная причина: scenario6 (частые транзакции)

Вторичный эффект: Рост transactionid блокировок (LOCK: 0.82)

Третичный эффект: Перегрузка LockManager (LWLOCK: 0.83)

Конечный результат: Падение производительности (SPEED-WAITINGS: -0.47)

Количественная оценка влияния:

  • На каждый 1% роста LOCK ожиданий, WAITINGS растут на 0.82%
  • На каждый 1% роста LWLOCK ожиданий, WAITINGS растут на 0.83%
  • На каждый 1% роста WAITINGS, SPEED падает на 0.47%

5.5. Согласованность с системными метриками

Полная согласованность данных:

1. CPU метрики подтверждают корреляции:

  • cs-sy = 0.9653 → Высокие переключения контекста из-за блокировок
  • cs-us = 0.8449 → Пользовательские процессы часто прерываются
  • procs_r > ядра (89.76%) → Процессы готовы, но не могут выполняться из-за блокировок

Согласованность:

Высокие LOCK/LWLOCK (0.82/0.83)

Процессы блокированы, но в run queue

Частые переключения контекста (cs-sy: 0.9653)

Рост system time, падение производительности

2. RAM метрики согласуются с отрицательной IO-корреляцией:

  • memory_free < 5% (85.54% наблюдений) → Активное кэширование
  • swap_si/so = 0 → Нет свопинга, данные в памяти
  • memory_cache = 6.25-6.61 GB → Огромный кэш данных

Согласованность:

Данные в кэше (memory_cache большой)

Физический IO не требуется (WAITINGS-IO: -0.72)

Но блокировки препятствуют доступу

Ожидания растут без увеличения IO

3. IO метрики идеально согласуются с корреляциями:

  • %util = 0.02-0.03% → Диски практически не используются
  • w_await = 0.95-49 мс → Отличное время отклика
  • aqu_sz = 0 → Нет очередей на дисках

Согласованность:

WAITINGS-IO = -0.72 (сильная отрицательная)

Ожидания не связаны с дисками

Системные метрики: %util < 1%, await < 5мс

Подтверждение: Диски не являются узким местом

Матрица согласованности:

-9

Ключевые выводы из согласованности:

  1. Данные непротиворечивы: Все метрики рисуют одну и ту же картину
  2. Проблема изолирована: Узкое место - блокировки, а не инфраструктура
  3. Эффект домино: Блокировки → очередь процессов → переключения контекста → падение производительности
  4. Ложные цели исключены: Диски, память, сетевые таймауты - не являются проблемой

Эмпирическое правило из корреляций:

Если:
- WAITINGS-LOCK > 0.8
- WAITINGS-LWLOCK > 0.8
- WAITINGS-IO < 0
- cs-sy > 0.9

То с вероятностью >95%:
Проблема в contention блокировок, а не в инфраструктуре

Заключение:

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