← Назад к вопросам

Были ли проблемы с Latency запросов

2.2 Middle🔥 191 комментариев
#JVM и управление памятью#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Были ли проблемы с Latency запросов

Проблемы с задержками (latency) запросов — это одна из самых частых проблем в production-окружении. Я столкнулся с несколькими типичными сценариями и способами их решения.

Типичные причины высокой латентности

1. Неоптимизированные SQL запросы

Проблема: N+1 queries

// ❌ Плохо: N+1 запросы
public List<User> getUsersWithOrders() {
    List<User> users = userRepository.findAll();  // 1 запрос
    for (User user : users) {
        user.getOrders();  // N дополнительных запросов!
    }
    return users;
}

// ✅ Хорошо: Eager loading
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
    List<User> findAllWithOrders();
}

// ✅ или через @EntityGraph
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();

2. Неиндексированные колонки

Проблема: Полное сканирование таблицы

// ❌ Медленно без индекса
SELECT * FROM orders WHERE status = "COMPLETED" AND created_at > ?

// ✅ Решение: создать составной индекс
CREATE INDEX idx_orders_status_date ON orders(status, created_at);

// В Hibernate можно указать индексы через аннотации
@Entity
@Table(indexes = {
    @Index(name = "idx_orders_status_date", 
           columnList = "status, created_at", unique = false)
})
public class Order {
    // ...
}

3. Неправильное использование connection pool

Проблема: Истощение connections

# Неправильная конфигурация (по умолчанию)
spring.datasource.hikari.maximum-pool-size=10  # Слишком мало!
spring.datasource.hikari.connection-timeout=30000  # Может истечь

# ✅ Оптимальная конфигурация
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000

# Мониторинг
logging.level.com.zaxxer.hikari=DEBUG  # Видим когда pool saturated

4. Сетевые задержки

// ❌ Проблема: синхронные вызовы API
public UserProfile buildProfile(Long userId) {
    User user = userService.getUser(userId);  // 50ms
    List<Order> orders = orderService.getOrders(userId);  // 100ms
    List<Review> reviews = reviewService.getReviews(userId);  // 75ms
    return new UserProfile(user, orders, reviews);  // Итого 225ms
}

// ✅ Решение: параллельные запросы
public UserProfile buildProfile(Long userId) {
    CompletableFuture<User> userFuture = 
        CompletableFuture.supplyAsync(() -> userService.getUser(userId));
    CompletableFuture<List<Order>> ordersFuture = 
        CompletableFuture.supplyAsync(() -> orderService.getOrders(userId));
    CompletableFuture<List<Review>> reviewsFuture = 
        CompletableFuture.supplyAsync(() -> reviewService.getReviews(userId));
    
    CompletableFuture.allOf(userFuture, ordersFuture, reviewsFuture).join();
    
    return new UserProfile(
        userFuture.join(), 
        ordersFuture.join(), 
        reviewsFuture.join()
    );  // Всего ~100ms (время самого медленного)
}

5. Проблемы с кешированием

// ❌ Без кеша: каждый раз ходим в БД
public Category getCategory(String name) {
    return categoryRepository.findByName(name);  // Медленно при частых вызовах
}

// ✅ С Redis кешем
@Cacheable(value = "categories", key = "#name")
public Category getCategory(String name) {
    return categoryRepository.findByName(name);
}

@CacheEvict(value = "categories", allEntries = true)
public void updateAllCategories() {
    // Очищаем кеш при обновлении
}

// ✅ Для критичных данных — in-memory кеш
public class CategoryCache {
    private final ConcurrentHashMap<String, Category> cache = new ConcurrentHashMap<>();
    private final CategoryRepository repo;
    private static final long TTL_MINUTES = 30;
    private final ScheduledExecutorService scheduler;
    
    public Category get(String name) {
        return cache.computeIfAbsent(name, key -> {
            Category cat = repo.findByName(key);
            // Время жизни через scheduled refresh
            return cat;
        });
    }
}

6. Проблемы с garbage collection

// Мониторинг GC pause time
public class LatencyMonitor {
    private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    private final GarbageCollectorMXBean gcBean = 
        ManagementFactory.getGarbageCollectorMXBeans().get(0);
    
    public void logGCMetrics() {
        long gcTime = gcBean.getCollectionTime();
        long gcCount = gcBean.getCollectionCount();
        System.out.println("GC Time: " + gcTime + "ms, Count: " + gcCount);
    }
}

// ✅ Оптимизация через JVM flags
// -XX:+UseG1GC (для Java 9+)
// -XX:MaxGCPauseMillis=200
// -Xms4G -Xmx4G (явное выделение памяти)

7. Медленные сторонние API

// ✅ Установить timeout
HttpClient httpClient = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(5))  // Connection timeout
    .build();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com"))
    .timeout(Duration.ofSeconds(10))  // Request timeout
    .build();

// ✅ Retry логика с exponential backoff
public <T> T callWithRetry(Supplier<T> supplier, int maxRetries) {
    for (int i = 0; i < maxRetries; i++) {
        try {
            return supplier.get();
        } catch (Exception e) {
            if (i == maxRetries - 1) throw e;
            long delay = 100 * (long) Math.pow(2, i);  // 100ms, 200ms, 400ms...
            Thread.sleep(delay);
        }
    }
    return null;
}

Мониторинг и диагностика

// Spring Boot Actuator для мониторинга латентности
@Component
public class LatencyMetricsCollector {
    private final MeterRegistry meterRegistry;
    
    public void recordQueryTime(String query, long duration) {
        Timer.builder("db.query.time")
            .tag("query", query)
            .register(meterRegistry)
            .record(duration, TimeUnit.MILLISECONDS);
    }
}

// Логирование медленных запросов
spring.jpa.properties.hibernate.generate_statistics=true
spring.jpa.properties.hibernate.use_sql_comments=true
logging.level.org.hibernate.stat=DEBUG

Вывод

Мост проблем с латентностью решается через:

  1. Правильные индексы на БД
  2. Оптимизация N+1 запросов через JOIN FETCH
  3. Кеширование горячих данных
  4. Параллелизм для независимых операций
  5. Мониторинг и профилирование
  6. Правильная конфигурация connection pool

Ключевой принцип: всегда измеряй перед оптимизацией!

Были ли проблемы с Latency запросов | PrepBro