Performance Optimization and Caching
Harry
· 13 Sep 2026
· 2 views
Finding Bottlenecks
Performance problems usually show up under load, with large datasets, or in poorly written queries. The most common sources are N+1 queries, missing indexes, large payloads, and misused caching.
Query Optimization
- Paginate every list.
- Index frequently queried columns:
static mapping = {
email index: true
}- Avoid eager loading of collections.
- Use joins deliberately instead of lazy N+1 loops.
Second-Level Caching
For frequently read reference data, enable GORM's second-level cache:
static mapping = {
cache true
}Method-Level Caching
@Cacheable("users")
def listUsers() {
User.list()
}Cache results of expensive methods, and always plan for cache invalidation.
HTTP Response Caching
For APIs and static responses, set cache headers:
response.setHeader("Cache-Control", "max-age=3600")Asynchronous Processing
Move heavy, non-blocking work off the request thread:
@Async
def sendEmail() { ... }Email, notifications, and report generation are classic async candidates.
Monitoring and Metrics
- Enable SQL logging during development to spot bad queries.
- Watch memory and CPU under load.
- Expose health checks via /actuator/health.
- Use Prometheus/Grafana or similar for production metrics.
Common Mistakes
- Caching everything, including data that changes constantly.
- Ignoring cache invalidation and stale data.
- Premature optimization before measuring.
Key Points
- Measure first; optimize second.
- Pagination and indexes are the biggest wins for databases.
- Cache read-heavy data and plan invalidation.
- Offload heavy work with @Async.
