Перейти к содержанию

Проблемы производительности

Диагностика и оптимизация производительности Р13.Орбита.


Запросы выполняются медленно

Симптомы

  • Обработка вопроса занимает > 30 секунд
  • Таймауты при выполнении запросов
  • CLI "висит" на этапе обработки
  • API возвращает 504 Gateway Timeout

Причина

  • Неэффективные SQL запросы к ClickHouse
  • Большой объем обрабатываемых данных
  • Медленный LLM провайдер
  • Недостаточно ресурсов (CPU/память)
  • Сетевая задержка к ClickHouse или LLM API

Диагностика

# 1. Проверьте использование ресурсов контейнером
docker stats orbita --no-stream

# 2. Проверьте логи для timing информации
docker logs orbita | grep -i "duration\|took\|elapsed"

# 3. Проверьте CPU и память на хосте
top
free -h

# 4. Проверьте сетевую задержку к ClickHouse
docker exec orbita ping -c 10 clickhouse

# 5. Проверьте latency к LLM API
time curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $LLM_API_KEY"

# 6. Посмотрите какой SQL генерируется
docker logs orbita | grep "SELECT\|FROM\|WHERE"

Решение

Оптимизация 1: Ограничение результатов

# Добавьте в .env
nano .env

# Ограничьте количество строк в результате
QUERY_MAX_ROWS=1000  # По умолчанию может быть больше

# Перезапустите
docker restart orbita

Оптимизация 2: Увеличение ресурсов контейнера

# Остановите контейнер
docker stop orbita
docker rm orbita

# Запустите с увеличенными лимитами
docker run -d \
  --name orbita \
  --restart unless-stopped \
  --cpus="2.0" \  # 2 CPU cores
  --memory="4g" \  # 4GB RAM
  --memory-swap="6g" \  # 6GB swap
  -p 8000:8000 \
  --env-file .env \
  -v $(pwd)/domains:/app/domains \
  -v $(pwd)/logs:/app/logs \
  your-registry.company.com/orbita:latest

Оптимизация 3: Connection pooling

# Увеличьте размер пула подключений к ClickHouse
nano .env

CLICKHOUSE_POOL_SIZE=20  # Увеличьте с дефолтного 10
CLICKHOUSE_POOL_MAX_OVERFLOW=10

docker restart orbita

Оптимизация 4: Кеширование результатов

# Включите Redis для кеширования
nano .env

REDIS_URL=redis://redis:6379/0
CACHE_TTL=3600  # Кешировать на 1 час

# Запустите Redis если еще не запущен
docker-compose up -d redis

docker restart orbita

Оптимизация 5: Используйте более быструю LLM модель

nano .env

# Вместо gpt-4 используйте gpt-3.5-turbo (быстрее и дешевле)
LLM_MODEL=gpt-3.5-turbo

# Или используйте локальный Ollama для еще большей скорости
LLM_BASE_URL=http://host.docker.internal:11434
LLM_MODEL=llama3

docker restart orbita

Профилактика

  • Используйте индексы в ClickHouse для частых запросов
  • Ограничивайте временные диапазоны в вопросах
  • Используйте агрегацию вместо детализированных данных
  • Настройте monitoring для отслеживания performance

Высокое использование CPU

Симптомы

docker stats orbita
# CPU: 180% (из 200% доступных)

Причина

  • Множество одновременных запросов
  • Тяжелые вычисления (особенно при создании визуализаций)
  • Неоптимальная работа LLM провайдера
  • Memory leak или бесконечный цикл

Диагностика

# Проверьте загрузку CPU
docker stats orbita --no-stream

# Проверьте процессы внутри контейнера
docker exec orbita top -b -n 1

# Проверьте количество запросов
docker logs orbita | grep "POST /api/v1/queries" | wc -l

# Проверьте логи на наличие ошибок/циклов
docker logs orbita --tail 100 | grep -i "error\|retry\|attempt"

Решение

Ограничить CPU usage

docker update --cpus="1.5" orbita
# Ограничит контейнер до 1.5 CPU cores

# Или пересоздайте контейнер с лимитом
docker stop orbita
docker rm orbita

docker run -d \
  --name orbita \
  --cpus="1.5" \
  --cpu-shares=1024 \
  -p 8000:8000 \
  --env-file .env \
  -v $(pwd)/domains:/app/domains \
  your-registry.company.com/orbita:latest

Включить rate limiting

nano .env

# Ограничьте количество запросов
API_RATE_LIMIT_ENABLED=true
API_RATE_LIMIT_REQUESTS=10  # 10 запросов
API_RATE_LIMIT_WINDOW=60    # за 60 секунд

docker restart orbita

Использовать worker процессы

# Увеличьте количество Uvicorn workers для лучшего распределения нагрузки
nano .env

API_WORKERS=4  # По умолчанию 1

docker restart orbita

Профилактика

  • Настройте rate limiting
  • Используйте load balancer для распределения нагрузки
  • Мониторьте CPU usage

Высокое использование памяти

Симптомы

docker stats orbita
# MEM USAGE: 3.8GB / 4GB (95%)
  • Out of Memory (OOM) killer завершает контейнер
  • Контейнер постоянно перезапускается

Причина

  • Большие датасеты загружаются в память
  • Memory leak в приложении
  • Слишком много одновременных запросов
  • Кеширование слишком большого объема данных

Диагностика

# Проверьте использование памяти
docker stats orbita --no-stream

# Проверьте логи OOM killer
dmesg | grep -i "killed process"

# Проверьте размер сохраненных датасетов
docker exec orbita du -sh ~/.orbita/

# Мониторьте память в реальном времени
docker stats orbita

Решение

Увеличить лимит памяти

docker update --memory="6g" orbita

# Или пересоздайте контейнер
docker stop orbita
docker rm orbita

docker run -d \
  --name orbita \
  --memory="6g" \
  --memory-swap="8g" \
  --memory-reservation="4g" \
  -p 8000:8000 \
  --env-file .env \
  -v $(pwd)/domains:/app/domains \
  your-registry.company.com/orbita:latest

Ограничить размер результатов

nano .env

# Ограничить количество строк
QUERY_MAX_ROWS=1000

# Ограничить размер ответа в байтах
QUERY_MAX_RESULT_SIZE_MB=100  # 100MB макс

docker restart orbita

Очистить старые данные

# Очистить старые датасеты
docker exec orbita python -c "
from orbita_service.dataset import DatasetService
from datetime import datetime, timedelta
service = DatasetService()
cutoff = datetime.now() - timedelta(days=30)
service.cleanup_old_datasets(cutoff)
"

# Или удалить вручную через SQL
docker exec postgres-metadata psql -U orbita_user orbita -c "
DELETE FROM datasets WHERE created_at < NOW() - INTERVAL '30 days';
"

Профилактика

  • Настройте автоматическую очистку старых датасетов
  • Используйте pagination для больших результатов
  • Не храните датасеты в памяти, используйте БД

Медленная генерация SQL

Симптомы

  • LLM долго генерирует SQL запрос (> 10 секунд)
  • Генерируется неоптимальный SQL

Причина

  • Используется медленная модель (GPT-4 vs GPT-3.5)
  • Слишком большой context window
  • Неоптимизированные few-shot примеры в доменах

Диагностика

# Проверьте какая модель используется
cat .env | grep LLM_MODEL

# Проверьте latency к LLM API
time curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4","messages":[{"role":"user","content":"test"}]}'

# Проверьте размер контекста в конфигурации доменов
wc -l domains/*.yaml

Решение

Используйте более быструю модель

nano .env

# GPT-3.5-turbo быстрее GPT-4
LLM_MODEL=gpt-3.5-turbo

# Или используйте локальный Ollama
LLM_BASE_URL=http://host.docker.internal:11434
LLM_MODEL=llama3

docker restart orbita

Оптимизируйте few-shot примеры

# domains/ecommerce.yaml
# Оставьте только самые релевантные примеры (3-5 штук)

few_shot_examples:
  - question: "покажи продажи за последний месяц"
    sql: "SELECT date, SUM(amount) FROM orders WHERE date >= today() - INTERVAL 1 MONTH GROUP BY date"
  # ... оставьте только ключевые примеры

Уменьшите temperature

nano .env

# Меньше temperature = более детерминированные и быстрые ответы
LLM_TEMPERATURE=0.1  # Вместо 0.7

docker restart orbita

Профилактика

  • Используйте кеширование сгенерированных SQL для похожих вопросов
  • Оптимизируйте few-shot examples
  • Используйте fine-tuned модели (если доступно)

Медленное выполнение SQL в ClickHouse

Симптомы

  • SQL генерируется быстро, но выполняется долго (> 30 секунд)

Причина

  • Неоптимальный SQL запрос
  • Отсутствие индексов в ClickHouse
  • Полное сканирование таблицы
  • Большой объем данных без фильтрации

Диагностика

# Проверьте SQL в логах
docker logs orbita | grep "Executing SQL"

# Выполните EXPLAIN в ClickHouse
clickhouse-client --query "
EXPLAIN SELECT * FROM ecommerce.orders WHERE date >= '2024-01-01'
"

# Проверьте размер таблицы
clickhouse-client --query "
SELECT
    table,
    formatReadableSize(sum(bytes)) as size,
    sum(rows) as rows
FROM system.parts
WHERE database = 'ecommerce'
GROUP BY table
"

# Проверьте медленные запросы
clickhouse-client --query "
SELECT
    query,
    query_duration_ms,
    read_rows
FROM system.query_log
WHERE type = 'QueryFinish'
    AND query_duration_ms > 1000
ORDER BY query_duration_ms DESC
LIMIT 10
"

Решение

Создать индексы в ClickHouse

-- Добавьте индексы на часто используемые колонки
ALTER TABLE ecommerce.orders ADD INDEX idx_date (date) TYPE minmax GRANULARITY 1;
ALTER TABLE ecommerce.orders ADD INDEX idx_customer (customer_id) TYPE set(1000) GRANULARITY 1;

-- Материализуйте индексы
ALTER TABLE ecommerce.orders MATERIALIZE INDEX idx_date;
ALTER TABLE ecommerce.orders MATERIALIZE INDEX idx_customer;

Оптимизировать таблицы ClickHouse

-- Используйте правильный engine и партиционирование
CREATE TABLE ecommerce.orders_optimized
(
    date Date,
    order_id UInt64,
    customer_id UInt64,
    amount Decimal(10,2)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (date, customer_id)
SETTINGS index_granularity = 8192;

-- Перенесите данные
INSERT INTO ecommerce.orders_optimized SELECT * FROM ecommerce.orders;

Ограничить временной диапазон

Научите пользователей формулировать вопросы с временными ограничениями:

❌ Плохо: "покажи все продажи"
✅ Хорошо: "покажи продажи за последний месяц"

Профилактика

  • Создавайте индексы на ключевые колонки
  • Используйте партиционирование по дате
  • Регулярно запускайте OPTIMIZE TABLE
  • Мониторьте медленные запросы

Долгое создание визуализаций

Симптомы

  • Создание графиков занимает > 20 секунд
  • Таймауты при генерации визуализаций

Причина

  • Слишком много точек данных на графике
  • Неоптимальная библиотека визуализации
  • Недостаточно ресурсов для matplotlib/plotly

Диагностика

# Проверьте размер датасета
docker exec orbita python -c "
from orbita_service.dataset import DatasetService
service = DatasetService()
dataset = service.get_dataset('dataset_id')
print(f'Rows: {len(dataset.data)}')
"

# Проверьте логи для timing
docker logs orbita | grep "chart\|visualization"

Решение

Ограничить количество точек данных

nano .env

# Ограничить количество точек на графике
CHART_MAX_POINTS=1000

docker restart orbita

Использовать агрегацию

Для больших датасетов используйте агрегацию перед визуализацией:

❌ Плохо: "покажи график всех транзакций"
✅ Хорошо: "покажи график продаж по дням"

Использовать более быструю библиотеку

nano .env

# Используйте matplotlib вместо plotly для статических графиков
CHART_BACKEND=matplotlib  # Быстрее чем plotly

docker restart orbita

Профилактика

  • Агрегируйте данные перед визуализацией
  • Используйте sampling для очень больших датасетов
  • Кешируйте сгенерированные графики

Контрольный список оптимизации

При проблемах с производительностью проверьте:

Инфраструктура

  • Достаточно CPU (минимум 2 cores)
  • Достаточно RAM (минимум 4GB)
  • SSD диски (не HDD)
  • Стабильная сеть к ClickHouse

Конфигурация

  • Connection pooling настроен
  • Limits установлены (QUERY_MAX_ROWS)
  • Rate limiting включен
  • Кеширование настроено (Redis)

ClickHouse

  • Индексы созданы
  • Таблицы оптимизированы
  • Партиционирование настроено
  • OPTIMIZE TABLE регулярно запускается

LLM

  • Используется оптимальная модель
  • Temperature понижена
  • Few-shot примеры минимизированы
  • Timeout увеличен при необходимости

Мониторинг производительности

Метрики для отслеживания

# 1. Время обработки запросов
docker logs orbita | grep "Request took" | awk '{print $NF}'

# 2. Использование ресурсов
docker stats orbita --no-stream

# 3. Количество запросов в секунду
docker logs orbita | grep "POST /api/v1/queries" | wc -l

# 4. Slow queries в ClickHouse
clickhouse-client --query "
SELECT query, query_duration_ms
FROM system.query_log
WHERE query_duration_ms > 5000
ORDER BY query_duration_ms DESC LIMIT 10
"

Настройка Prometheus (опционально)

Если используете мониторинг через Prometheus:

# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    depends_on:
      - prometheus

Следующие шаги