Dlaczego Connection Pooling Jest Kluczowy
Wyobraź sobie restaurację gdzie każdy gość musi zbudować swój własny stolik przed jedzeniem, a po posiłku go rozebrać. Absurdalne, prawda? Dokładnie tak działa aplikacja bez connection poolingu – każde zapytanie do bazy tworzy nowe połączenie od zera.
Tworzenie połączenia z bazą danych to kosztowna operacja obejmująca nawiązanie połączenia TCP, uwierzytelnienie, inicjalizację sesji. W aplikacjach web gdzie mamy setki zapytań na sekundę, to prowadzi prosto do katastrofy wydajnościowej.
Co się nauczysz:
- Czym jest connection pooling i jak działa
- Jak skonfigurować pooling w Spring Boot 2.x
- Popularne implementacje: HikariCP, Apache DBCP
- Najważniejsze parametry konfiguracyjne
- Typowe problemy i ich rozwiązania
Jak Działa Connection Pool
Zasada działania jest prosta:
1. **Inicjalizacja:** Aplikacja tworzy pulę N połączeń na starcie
2. **Zapytanie:** Kod biznesowy „pożycza” połączenie z puli
3. **Wykonanie:** Wykonuje zapytanie SQL
4. **Zwrot:** Połączenie wraca do puli (nie jest zamykane!)
5. **Czyszczenie:** Pool okresowo sprawdza i usuwa nieaktywne połączenia
Konfiguracja w Spring Boot 2.x
Spring Boot 2.x domyślnie używa HikariCP – najszybszego connection poola w Javie:
# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/myapp username: app_user password: secret_password # HikariCP konfiguracja (domyślna w Spring Boot 2.x) hikari: maximum-pool-size: 20 # Maksymalna liczba połączeń minimum-idle: 5 # Minimalne połączenia w spoczynku idle-timeout: 300000 # 5 minut - timeout dla nieaktywnych max-lifetime: 1800000 # 30 minut - maksymalny czas życia połączenia connection-timeout: 20000 # 20 sekund - timeout nawiązywania połączenia validation-timeout: 5000 # Timeout walidacji połączenia leak-detection-threshold: 60000 # Wykrywanie "wycieków" połączeń
Programmatyczna Konfiguracja
@Configuration public class DatabaseConfig { @Bean @Primary @ConfigurationProperties("spring.datasource.hikari") public DataSource dataSource() { HikariConfig config = new HikariConfig(); // Podstawowe ustawienia połączenia config.setJdbcUrl("jdbc:mysql://localhost:3306/myapp"); config.setUsername("app_user"); config.setPassword("secret_password"); // Konfiguracja poolingu config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setIdleTimeout(300000); config.setMaxLifetime(1800000); // Monitoring i debugging config.setPoolName("MyAppHikariPool"); config.setLeakDetectionThreshold(60000); return new HikariDataSource(config); } }
Kluczowe Parametry Konfiguracyjne
Parametr | Opis | Domyślna wartość | Rekomendacja |
---|---|---|---|
maximum-pool-size | Maksymalna liczba połączeń w puli | 10 | 15-25 dla web apps |
minimum-idle | Minimalne połączenia zawsze dostępne | 10 | 25% max-pool-size |
idle-timeout | Czas po którym nieużywane połączenie jest zamykane | 600s | 300s (5 min) |
max-lifetime | Maksymalny czas życia połączenia | 1800s | 1800s (30 min) |
connection-timeout | Timeout na pobranie połączenia z puli | 30s | 20s |
Popularne Implementacje Connection Poolów
1. HikariCP (Domyślny w Spring Boot 2.x)
com.zaxxer HikariCP
**Zalety:** Najszybszy, nowoczesny, doskonały monitoring
**Wady:** Relatywnie nowy (ale już stabilny w 2018)
2. Apache Commons DBCP2
org.apache.commons commons-dbcp2
spring: datasource: type: org.apache.commons.dbcp2.BasicDataSource dbcp2: max-total: 20 max-idle: 8 min-idle: 0 max-wait-millis: 10000
**Zalety:** Stabilny, sprawdzony w battleach, dużo opcji konfiguracyjnych
**Wady:** Wolniejszy od HikariCP
3. Tomcat JDBC Pool
spring: datasource: type: org.apache.tomcat.jdbc.pool.DataSource tomcat: max-active: 20 max-idle: 8 min-idle: 0 max-wait: 10000
**Zalety:** Lightweight, dobra wydajność
**Wady:** Mniej funkcji niż DBCP2
Typowe Problemy i Rozwiązania
1. Connection Leaks
// ❌ ŹLEJ - connection leak! public ListgetUsers() { Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users"); ResultSet rs = stmt.executeQuery(); List users = new ArrayList<>(); while(rs.next()) { users.add(mapToUser(rs)); } // Brak zamknięcia resources! return users; } // ✅ DOBRZE - try-with-resources public List getUsers() { List users = new ArrayList<>(); try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users"); ResultSet rs = stmt.executeQuery()) { while(rs.next()) { users.add(mapToUser(rs)); } } catch (SQLException e) { throw new DataAccessException("Failed to fetch users", e); } return users; }
2. Pool Exhaustion
# Monitoring pool exhaustion spring: datasource: hikari: leak-detection-threshold: 60000 # Loguje połączenia trzymane > 60s register-mbeans: true # Włącza JMX monitoring
Monitoring Connection Pool
@Component public class PoolMonitor { @Autowired private DataSource dataSource; @Scheduled(fixedRate = 30000) // Co 30 sekund public void logPoolStats() { if (dataSource instanceof HikariDataSource) { HikariDataSource hikariDS = (HikariDataSource) dataSource; HikariPoolMXBean poolMXBean = hikariDS.getHikariPoolMXBean(); log.info("Pool stats - Active: {}, Idle: {}, Total: {}, Waiting: {}", poolMXBean.getActiveConnections(), poolMXBean.getIdleConnections(), poolMXBean.getTotalConnections(), poolMXBean.getThreadsAwaitingConnection() ); } } }
Zależy od aplikacji. Zacznij od formuły: (liczba CPU cores * 2) + liczba dysków. Dla typowej web app: 15-25 połączeń. Zawsze testuj pod obciążeniem!
Nie – każda transakcja dostaje dedykowane połączenie z puli. Pool zarządza tylko dostępnością połączeń, nie ich stanem transakcyjnym.
Sprawdź max_connections w MySQL/PostgreSQL. Pool może chcieć więcej połączeń niż baza pozwala. Zmniejsz maximum-pool-size lub zwiększ limit w bazie.
Włącz leak-detection-threshold w HikariCP. Loguje stack trace gdzie połączenie zostało „pożyczone” ale nie zwrócone. Pomoże znaleźć kod który nie zamyka resources.
Tak! Czasem przydatne dla read/write separation lub różnych baz danych. W Spring Boot użyj @Primary i @Qualifier do rozróżniania.
🚀 Zadanie dla Ciebie
Stwórz Spring Boot aplikację z HikariCP pool. Skonfiguruj maksymalnie 5 połączeń i stwórz endpoint który „celowo” nie zamyka połączeń. Zaobserwuj jak aplikacja zachowuje się po wyczerpaniu puli. Następnie napraw kod i dodaj monitoring pool statistics.
Przydatne zasoby:
Jakie są Twoje doświadczenia z connection poolingiem? Czy spotkałeś się z problemami wydajnościowymi związanymi z połączeniami do bazy?