Аналіз вузьких місць (Bottleneck Analysis) за результатами навантажувальних тестів

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Аналіз вузьких місць (Bottleneck Analysis) за результатами навантажувальних тестів
Середня
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Аналіз вузьких місць за результатами нагрузного тесту

Нагрузний тест показав деградацію — залишилось знайти причину. Послідовний аналіз: мережевий рівень → програма → база даних → інфраструктура. Вузьке місце завжди одне: виправити його, запустити тест знову.

Діагностична послідовність

Висока latency або помилки
        │
        ├── Висока p95 latency, CPU < 70%, пам'ять ОК
        │   └── → База даних: повільні запити, блокування, N+1
        │
        ├── CPU 90–100%, latency растет пропорційно
        │   └── → Обчислювальний bottleneck: профілювати CPU-hot paths
        │
        ├── Пам'ять растет, swap активний
        │   └── → Витік пам'яти або heap занадто малий
        │
        ├── ENOMEM / EMFILE / ECONNREFUSED
        │   └── → Системні ліміти: ulimit, file descriptors, TCP backlog
        │
        └── 502/504 помилки, програма ОК
            └── → Nginx upstream, load balancer timeout

Аналіз PostgreSQL під навантаженням

-- Запущені запити прямо зараз (виконувати під час тесту)
SELECT pid, now() - query_start AS duration,
       state, wait_event_type, wait_event,
       left(query, 100) AS query_preview
FROM pg_stat_activity
WHERE state != 'idle'
  AND query NOT LIKE '%pg_stat_activity%'
ORDER BY duration DESC;

-- Блокування: хто кого блокує
SELECT blocked.pid, blocked.query,
       blocking.pid AS blocking_pid,
       blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
  ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.cardinality(pg_blocking_pids(blocked.pid)) > 0;

-- Найважчі запити (pg_stat_statements)
SELECT query, calls, mean_exec_time, total_exec_time,
       stddev_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

-- Відсутні індекси: sequential scans на великих таблицях
SELECT relname, seq_scan, seq_tup_read,
       idx_scan, seq_tup_read / nullif(seq_scan, 0) AS avg_rows_per_seqscan
FROM pg_stat_user_tables
WHERE seq_scan > 100
  AND seq_tup_read > 10000
ORDER BY seq_tup_read DESC;

Node.js: CPU профілювщик

// server.js — вмикнути V8 profiling через сигнал
process.on('SIGUSR1', () => {
  const { Session } = require('inspector')
  const session = new Session()
  session.connect()

  session.post('Profiler.enable')
  session.post('Profiler.start')

  // Профілювати 30 секунд
  setTimeout(() => {
    session.post('Profiler.stop', (err, { profile }) => {
      require('fs').writeFileSync('./cpu-profile.cpuprofile', JSON.stringify(profile))
      console.log('CPU profile saved to cpu-profile.cpuprofile')
      session.disconnect()
    })
  }, 30000)
})

// Запустити під навантаженням: kill -USR1 <pid>
// Відкрити в Chrome DevTools → More Tools → JavaScript Profiler
# Альтернатива: 0x flamegraph
npm install -g 0x
0x --output-dir profile node server.js &
APP_PID=$!

# Запустити k6 тест
k6 run tests/load/main.js

# Зупинити та отримати flamegraph
kill -USR2 $APP_PID
# Відкриється flamegraph.html у браузері

Flamegraph: що шукати

Широкі плоскі смуги в середині — довга CPU-робота. Типові знахідки:

  • JSON.parse/stringify в hot path — переключитися на streaming або schema-based serializer
  • bcrypt з високим cost factor — зменшити cost або кешувати сесії
  • Regex без компіляції — винести за межі функції
  • Синхронна файлова операція (fs.readFileSync) у запиті

Python: профілювщик під навантаженням

# pyinstrument — non-intrusive profiler для production
pip install pyinstrument

# Middleware для Flask
from pyinstrument import Profiler
from flask import request, g

@app.before_request
def start_profiler():
    if request.args.get('profile') == 'true':
        g.profiler = Profiler()
        g.profiler.start()

@app.after_request
def stop_profiler(response):
    if hasattr(g, 'profiler'):
        g.profiler.stop()
        # Вернути HTML-звіт у відповіді
        response.data = g.profiler.output_html()
        response.content_type = 'text/html'
    return response

# Запит з профілюванням: GET /api/posts?profile=true

Аналіз пула соединень

# Моніторинг pgBouncer під час тесту
psql -h localhost -p 6432 pgbouncer -c "SHOW POOLS;"

# Що дивитися:
# cl_active: клієнти активно працюють
# cl_waiting: клієнти чекають соединення (>0 = проблема)
# sv_active: серверні соединення активні
# sv_idle: простаювальні соединення у пулі
# maxwait: максимальний час очікування (сек)
-- PostgreSQL: статистика пула соединень
SELECT datname, count(*) AS total_connections,
       count(*) FILTER (WHERE state = 'active') AS active,
       count(*) FILTER (WHERE state = 'idle') AS idle,
       count(*) FILTER (WHERE wait_event_type = 'Lock') AS waiting_lock
FROM pg_stat_activity
GROUP BY datname;

Аналіз результатів k6: знайти момент деградації

# parse_k6_results.py
import json
import pandas as pd

def find_degradation_point(json_results: str):
    """Знайти момент деградації по часовому ряду метрик"""
    records = []

    with open(json_results) as f:
        for line in f:
            try:
                record = json.loads(line)
                if record.get('type') == 'Point':
                    records.append({
                        'timestamp': record['data']['time'],
                        'metric': record['metric'],
                        'value': record['data']['value']
                    })
            except:
                continue

    df = pd.DataFrame(records)
    df['timestamp'] = pd.to_datetime(df['timestamp'])

    # Аналізувати p95 latency по 1-хвилинних вікнах
    p95_df = df[df['metric'] == 'http_req_duration'].copy()
    p95_df = p95_df.set_index('timestamp').resample('1min')['value'].quantile(0.95)

    # Знайти першу хвилину, коли p95 перевищив поріг
    threshold = 500  # ms
    degradation = p95_df[p95_df > threshold]

    if not degradation.empty:
        print(f"Degradation detected at: {degradation.index[0]}")
        print(f"p95 at degradation: {degradation.iloc[0]:.0f}ms")
    else:
        print("No degradation detected (all within threshold)")

    return p95_df

Типові оптимізації після аналізу

Вузьке місце Симптом Рішення
N+1 запити до БД DB active queries >> VU count DataLoader / eager loading / JOIN
Відсутній індекс SeqScan на великій таблиці CREATE INDEX CONCURRENTLY
Повільна JSON серіалізація CPU висока, hot path в serialize Protobuf / simdjson / msgpack
Connection pool overflow cl_waiting > 0 у pgBouncer Збільшити pool_size або додати replicas
GC паузи Spiky latency без CPU навантаження Збільшити heap, tune GC flags
Блокування таблиць wait_event = Lock у pg_stat Оптимізувати порядок операцій, NOWAIT

Часовий графік

Повний аналіз вузьких місць за результатами нагрузного тесту з рекомендаціями та верифікаційним тестом — 1–2 робочих дні.