GitHub - Комплекс pg_expecto для статистического анализа производительности и нагрузочного тестирования СУБД PostgreSQL
Подготовка эксперимента.
Задача эксперимента
Проанализировать метрики производительности СУБД и инфраструктуры в ходе нагрузочного тестирования при значении параметра checkpoint_timeout = 10 минут.
Исходные данные
Регрессионный и корреляционный анализ
Операционная скорость СУБД
Важная деталь по графикам
Периодические провалы операционной скорости это не влияние checkpoint , это длительное выполнение конкурентных update в сценарии-6, в то время как остальные сценарии завершили работу.
Ожидания СУБД
Ожидания СУБД типа IO
Ожидания СУБД типа LWLock
Типы ожиданий СУБД (Диаграмма Парето)
События ожиданий по тестовым запросам (Диаграмма Парето)
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:
- LOCK (блокировки): +0.82 (сильная положительная)
- LWLOCK (легковесные блокировки): +0.83 (сильная положительная)
- IO (ввод-вывод): -0.72 (сильная отрицательная)
- 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% наблюдений
Рекомендации для первоочередного анализа:
- Исследовать scenario6 - главный источник блокировок
- Проанализировать распределение блокировок - проверить long-running transactions
- Оптимизировать настройки блокировок - возможно, уменьшить max_connections с 1000
- Добавить мониторинг contention - отслеживать wait events в реальном времени
- Рассмотреть увеличение 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 события:
- transactionid - ожидание завершения транзакций
- tuple - блокировки на уровне строк
LWLock события:
- BufferContent - конкуренция за доступ к буферам данных
- BufferMapping - конкуренция за маппинг буферов
- LockManager - конкуренция за менеджер блокировок (наиболее частый!)
- pg_stat_statements - блокировки при записи статистики
- ProcArray - конкуренция за доступ к массиву процессов
- XidGen - конкуренция за генерацию ID транзакций
Гипотезы о природе scenario6:
- Тяжелая запись/обновление - множественные операции UPDATE/DELETE
- Длительные транзакции - удерживающие блокировки продолжительное время
- Высокая конкуренция за строки - много процессов пытаются изменить одни и те же данные
- Частые коммиты - создающие нагрузку на LockManager и XidGen
Дополнительные улики:
- 129,514 выполнений за ~3 часа = ~12 выполнений в секунду в среднем
- Высокая нагрузка на ProcArray указывает на много конкурентных процессов
- BufferContent/BufferMapping указывают на активную работу с данными
2.4. Рекомендации по оптимизации запросов и настройке СУБД
А. Оптимизация scenario6:
- Анализ плана выполнения:textEXPLAIN (ANALYZE, BUFFERS, VERBOSE) select scenario6();
- Возможные оптимизации запроса:
Разделить на более мелкие транзакции
Использовать FOR UPDATE SKIP LOCKED для избежания блокировок ожидания
Переписать с использованием CTE или временных таблиц для уменьшения времени блокировок
Добавить индексы для уменьшения времени поиска строк - Изоляция транзакций:
Проверить возможность использования 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
В. Архитектурные изменения:
- Partitioning - разделить горячие таблицы на партиции
- Sharding - распределить данные по нескольким серверам
- Read Replicas - перенести чтение на реплики
- Connection Pooling - использовать пул соединений (например, PgBouncer)
- Queue-based Processing - использовать очереди (например, RabbitMQ) для асинхронной обработки
Г. Рекомендации по разработке:
- Retry Logic - реализовать повторные попытки при deadlock
- Exponential Backoff - использовать экспоненциальную задержку между повторами
- Smaller Transactions - разбивать большие транзакции на мелкие
- Lock Timeout - устанавливать разумные таймауты блокировокsqlSET lock_timeout = '5s';
Критический путь оптимизации:
- Сначала - проанализировать и оптимизировать scenario6
- Затем - настроить параметры PostgreSQL для снижения конкуренции
- Параллельно - мониторить и настраивать инфраструктуру
- В долгосрочной перспективе - рассмотреть архитектурные изменения
Показатели успеха:
- Снижение 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 процессов
Причины:
- Высокий contention блокировок в PostgreSQL:
Процессы готовы к выполнению, но ожидают освобождения блокировок
Активное переключение контекста между процессами, ожидающими одних ресурсов - Настройки PostgreSQL:
max_connections = 1000 - слишком высокое значение для 8 ядер
max_parallel_workers = 16 - может создавать излишнюю конкуренцию
max_worker_processes = 16 - дополнительная нагрузка на планировщик - Характер нагрузки:
Большое количество конкурентных транзакций (особенно scenario6)
Частые переключения контекста из-за блокировок
Высокая корреляция переключений контекста (cs) с system time (sy) и user time (us)
Коэффициенты корреляции:
- cs-sy: 0.9653 (очень высокая)
- cs-us: 0.8449 (высокая)
- cs-in: 0.6647 (средняя)
Интерпретация:
- cs-sy (0.9653):
Почти идеальная линейная зависимость
Ядро тратит огромное время на переключение контекста вместо полезной работы
Причина: Слишком много процессов конкурируют за CPU из-за блокировок - cs-us (0.8449):
Высокая корреляция с пользовательским временем
Пользовательские процессы часто прерываются и возобновляются
Причина: Активное перепланирование из-за блокировок - Статистика переключений контекста:
MIN: 692 переключений/сек
MAX: 33,912 переключений/сек (очень высокое значение)
В среднем: ~15-20 тыс. переключений/сек при нагрузке
Признаки нехватки CPU:
- Косвенные признаки:
Очередь процессов постоянно превышает число ядер
Высокое user time (us до 95%)
Очень высокие переключения контекста - Прямые признаки:
cpu_us + cpu_sy = 75.9% наблюдений превышает 80% (ALARM в отчете)
procs_r превышает ядра в 89.76% наблюдений
Низкий cpu_id (простой CPU): часто 0-2% - Вывод: 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% свободно
Причины:
- PostgreSQL shared_buffers = 2GB - занимает значительную часть памяти
- Системный кэш активно используется:
memory_cache: 6.25-6.61 GB (основной потребитель)
memory_buff: 478-497 MB - Рабочая нагрузка интенсивно использует кэширование
Оценка ситуации:
- Не критично: Система активно использует память для кэширования
- Опасно: При резком увеличении нагрузки может начаться свопинг
- Эффективно: Отсутствие свопинга указывает на хорошую настройку памяти
Признаки свопинга (si, so)
Данные:
- swap_si (swap in): 0 во всех наблюдениях
- swap_so (swap out): 0 во всех наблюдениях
- memory_swpd (используемый swap): 58 MB (константа, минимальное значение)
Вывод: Свопинг не используется, что хорошо:
- Память настроена адекватно рабочей нагрузке
- PostgreSQL не вытесняет страницы на диск
- Система работает в рамках доступной памяти
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 (отсутствует)
Интерпретация:
- Несоответствие: В СУБД есть IO ожидания, но системные метрики показывают нулевую загрузку дисков
- Возможные объяснения:
Ожидания связаны с логическим IO (буферы в памяти), а не физическим
Конкуренция за доступ к буферному кэшу
Ожидания на уровне файловой системы, а не диска
Признаки проблем с дисковой подсистемой
Отсутствуют:
- %util < 1% (при проблемах > 50-70%)
- await < 5 мс (при проблемах > 10-20 мс)
- Очереди отсутствуют (aqu_sz = 0)
- Нет чтения (r/s = 0), только минимальная запись
Вывод: Дисковая подсистема не является узким местом
3.4. Общие выводы и связь с ожиданиями СУБД
Иерархия узких мест:
- Первичное узкое место: Блокировки в PostgreSQL (LOCK/LWLOCK)
Создают каскадный эффект
Приводят к росту очереди процессов - Вторичное узкое место: CPU
Вызвано переключениями контекста из-за блокировок
Очередь процессов > числа ядер - Третичное узкое место: Память
Минимальный свободный объем, но без свопинга
Потенциальный риск при росте нагрузки - Не является узким местом: IO
Диски практически простаивают
Отличные времена отклика
Связь системных метрик с ожиданиями СУБД:
Цепочка причинно-следственных связей:
scenario6 (частые транзакции)
↓
Высокий contention блокировок (transactionid, LockManager)
↓
Процессы ожидают в run queue (procs_r ↑)
↓
Частые переключения контекста (cs ↑)
↓
Рост system time (sy ↑) за счет планировщика
↓
Снижение полезной работы CPU (us ↓ относительно)
↓
Общее падение производительности (SPEED ↓)
Подтверждение из метрик:
- procs_r до 56 при 8 ядрах → процессы блокированы
- cs до 33,912/сек → активное перепланирование
- cpu_sy коррелирует с cs (0.9653) → ядро занято переключениями
- Отсутствие IO нагрузки → проблема не в дисках
- Минимальная свободная память → система использует кэши, но не свопит
Рекомендации по инфраструктуре:
- CPU (высокий приоритет):
Увеличить количество ядер (16+)
Рассмотреть CPU с большим кэшем L3
Настроить CPU affinity для процессов PostgreSQL - Память (средний приоритет):
Увеличить до 16-32 GB
Позволит увеличить shared_buffers и кэши
Снизит риск свопинга при пиках - Настройка ОС:# Увеличить лимиты для 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 - Мониторинг:
Внедрить 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/сек)
Рекомендации:
- Увеличить до 16 ядер - это снизит нагрузку на планировщик
- Выбрать CPU с большим кэшем L3 (512KB L1, 32MB L2, 128MB L3 сейчас)
- Настроить CPU affinity/pinning:# Привязка PostgreSQL к определенным ядрам
taskset -cp 0-7,16-23 $(head -1 /var/lib/postgresql/*/postmaster.pid) - Включить CPU governor performance mode:bashcpupower frequency-set -g performance
Б. RAM - СРЕДНИЙ ПРИОРИТЕТ:
Текущее состояние: 7.5 GB, свободно <5% в 85.54% наблюдений
Рекомендации:
- Увеличить до 16-32 GB - позволит:
Увеличить shared_buffers до 4-8GB
Увеличить системный кэш
Исключить риск свопинга - Настройка параметров ядра: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мс)
Рекомендации:
- Текущая конфигурация адекватна
- Рассмотреть SSD/NVMe для WAL (vdc) - хотя сейчас не требуется
- Настройка планировщика 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. Наиболее сильные корреляции
Положительные корреляции:
- WAITINGS - LWLOCK: 0.83 (очень сильная)
- WAITINGS - LOCK: 0.82 (очень сильная)
- cs - sy: 0.9653 (почти идеальная) - из vmstat
- cs - us: 0.8449 (очень сильная) - из vmstat
- cs - in: 0.6647 (сильная) - из vmstat
Отрицательные корреляции:
- WAITINGS - IO: -0.72 (сильная отрицательная)
- SPEED - WAITINGS: -0.47 (умеренная отрицательная)
- WAITINGS - TIMEOUT: -0.39 (умеренная отрицательная)
Нулевые корреляции:
- WAITINGS - BUFFERPIN: 0.0000
- WAITINGS - EXTENSION: 0.0000
- WAITINGS - IPC: 0.0000
- 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-ожидания снижаются.
Объяснения:
- Эффект блокировки: Процессы не могут прогрессировать из-за блокировок, поэтому не доходят до операций IOtextПроцесс блокирован → Не выполняет работу → Не генерирует IO
- Кэширование в памяти: Данные уже в буферном кэше, но доступ блокированtextДанные в памяти (IO не требуется) → Но заблокированы → Ожидания растут, IO нет
- Подтверждение из системных метрик:
io_bi (blocks in) = 0 во всех наблюдениях
io_bo (blocks out) = 94-110 (минимальные значения)
Диски практически не используются (%util < 1%)
WAITINGS - TIMEOUT (-0.39):
Менее сильная, но значимая отрицательная корреляция.
Объяснение:
- Таймауты возникают при ожидании внешних ресурсов или при сетевых операциях
- В данной системе основной проблемой являются внутренние блокировки, а не внешние ресурсы
- Когда процессы блокированы на внутренних ресурсах, они не успевают достичь точек, где могли бы возникнуть таймауты
5.4. Как корреляции помогают понять природу узких мест?
Диагностика по корреляционной матрице:
- Исключение ложных причин:
WAITINGS-IO = -0.72 → Исключаем диски как узкое место
WAITINGS-BUFFERPIN = 0.00 → Исключаем конкуренцию за буферы
WAITINGS-IPC = 0.00 → Исключаем проблемы межпроцессного взаимодействия - Подтверждение реальных причин:
WAITINGS-LOCK = 0.82 → Подтверждаем блокировки транзакций
WAITINGS-LWLOCK = 0.83 → Подтверждаем конкуренцию за менеджер блокировок
SPEED-WAITINGS = -0.47 → Подтверждаем, что ожидания снижают производительность - Построение цепочки причинно-следственных связей:
Первичная причина: 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мс
↓
Подтверждение: Диски не являются узким местом
Матрица согласованности:
Ключевые выводы из согласованности:
- Данные непротиворечивы: Все метрики рисуют одну и ту же картину
- Проблема изолирована: Узкое место - блокировки, а не инфраструктура
- Эффект домино: Блокировки → очередь процессов → переключения контекста → падение производительности
- Ложные цели исключены: Диски, память, сетевые таймауты - не являются проблемой
Эмпирическое правило из корреляций:
Если:
- WAITINGS-LOCK > 0.8
- WAITINGS-LWLOCK > 0.8
- WAITINGS-IO < 0
- cs-sy > 0.9
То с вероятностью >95%:
Проблема в contention блокировок, а не в инфраструктуре
Заключение:
Корреляционный анализ не только подтверждает гипотезу о блокировках как основном узком месте, но и предоставляет количественные меры влияния каждого фактора, что позволяет приоритизировать оптимизации и прогнозировать эффект от изменений.