Performance is a feature. Here's how we took a Spring Boot API from 200ms average response time to 12ms.
Step 1: Database Query Optimization
The biggest wins usually come from here. We:
- Added composite indexes for frequent query patterns
- Replaced N+1 queries with JOIN FETCH
- Implemented pagination with keyset pagination instead of OFFSET
- Used @EntityGraph for selective lazy loading
Result: 200ms → 80ms
Step 2: Redis Caching
We added a Redis caching layer for frequently accessed, rarely changing data:
- Spring Cache with @Cacheable annotations
- Cache invalidation strategy using @CacheEvict
- TTL-based expiration for stale data prevention
Result: 80ms → 35ms
Step 3: Connection Pool Tuning
HikariCP configuration optimization:
- Set minimumIdle to match expected concurrent requests
- MaximumPoolSize = (2 * CPU cores) + disk spindles
- Enabled leak detection for debugging
Result: 35ms → 22ms
Step 4: JVM and GC Tuning
- Switched to G1GC with appropriate pause time targets
- Set heap size to 70% of available RAM
- Enabled JIT compilation with GraalVM Native Image for startup
Result: 22ms → 12ms
Profiling Tools We Use
- Spring Boot Actuator for runtime metrics
- Micrometer + Prometheus + Grafana for monitoring
- JProfiler for deep profiling
- Apache JMeter for load testing