AWS ElastiCache: Redis and Memcached Caching Strategies (2026)
ElastiCache is AWS's managed in-memory caching service — eliminating the operational burden of managing Redis or Memcached clusters. A well-placed cache can reduce database load by 80%+ and cut API response times from hundreds of milliseconds to single-digit milliseconds. This guide covers the Redis vs Memcached decision, cluster configuration, caching patterns, and Spring Boot integration.
Table of Contents
1. Redis vs Memcached
| Feature | Redis | Memcached |
|---|---|---|
| Data structures | String, Hash, List, Set, ZSet, Stream, Bitmap | String only |
| Persistence | RDB snapshots + AOF logs | None |
| Replication | Yes (primary + replicas) | No |
| Multi-AZ | Yes (automatic failover) | No |
| Pub/Sub | Yes | No |
| Lua scripting | Yes | No |
| Multi-threading | Limited (6.0+ for I/O) | Yes (built-in) |
| Use Memcached when | Simple caching, multi-threaded scaling needed, no persistence required, simplest possible setup | |
2. Creating an ElastiCache Redis Cluster
# Create subnet group (must be in same VPC as your app)
aws elasticache create-cache-subnet-group \
--cache-subnet-group-name myapp-cache-subnets \
--cache-subnet-group-description "ElastiCache subnets" \
--subnet-ids subnet-private-1a subnet-private-1b
# Create Redis replication group (primary + 1 replica, Multi-AZ)
aws elasticache create-replication-group \
--replication-group-id myapp-redis \
--replication-group-description "MyApp production cache" \
--cache-node-type cache.t3.medium \
--engine redis \
--engine-version 7.2 \
--num-cache-clusters 2 \
--cache-subnet-group-name myapp-cache-subnets \
--security-group-ids sg-redis-cache \
--automatic-failover-enabled \
--multi-az-enabled \
--at-rest-encryption-enabled \
--transit-encryption-enabled \
--auth-token "$(aws secretsmanager get-secret-value --secret-id myapp/redis/token --query SecretString --output text)"
3. Cluster Mode
Cluster mode disabled (default): one primary with up to 5 replicas in one shard. Simple setup, good for most workloads.
Cluster mode enabled: data sharded across up to 500 shards, each with replicas. Use when your dataset exceeds a single node's memory or you need write scaling:
# Cluster mode enabled — 3 shards, 1 replica each
aws elasticache create-replication-group \
--replication-group-id myapp-redis-cluster \
--replication-group-description "Clustered Redis" \
--cache-node-type cache.r6g.large \
--engine redis \
--engine-version 7.2 \
--num-node-groups 3 \
--replicas-per-node-group 1 \
--automatic-failover-enabled \
--cache-subnet-group-name myapp-cache-subnets \
--security-group-ids sg-redis-cache
{user}.sessions and {user}.profile to force related keys to the same shard.4. Caching Patterns
Cache-Aside (Lazy Loading)
@Service
public class ProductService {
private final RedisTemplate<String, Product> redisTemplate;
private final ProductRepository productRepository;
public Product getProduct(Long id) {
String cacheKey = "product:" + id;
// 1. Check cache
Product cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) return cached;
// 2. Cache miss — load from DB
Product product = productRepository.findById(id)
.orElseThrow(() -> new NotFoundException("Product not found: " + id));
// 3. Store in cache with TTL
redisTemplate.opsForValue().set(cacheKey, product, Duration.ofMinutes(30));
return product;
}
public void updateProduct(Long id, Product updated) {
productRepository.save(updated);
// Invalidate cache on update
redisTemplate.delete("product:" + id);
}
}
Write-Through
public Product updateProduct(Long id, Product updated) {
// Write to DB AND cache simultaneously
Product saved = productRepository.save(updated);
redisTemplate.opsForValue().set("product:" + id, saved, Duration.ofHours(1));
return saved;
}
5. Spring Boot Integration
# application.yaml
spring:
data:
redis:
host: myapp-redis.abc123.use1.cache.amazonaws.com
port: 6379
ssl:
enabled: true
password: ${REDIS_AUTH_TOKEN}
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withCacheConfiguration("products", config.entryTtl(Duration.ofHours(1)))
.withCacheConfiguration("users", config.entryTtl(Duration.ofMinutes(5)))
.build();
}
}
// Use Spring Cache annotations
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProduct(Long id) { ... }
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) { ... }
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) { ... }
}
6. Session Storage
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
spring:
session:
store-type: redis
timeout: 30m
redis:
namespace: myapp:session
This stores all HTTP sessions in Redis — enabling stateless application instances and sticky-session-free load balancing.
Frequently Asked Questions
What cache.t3.medium vs cache.r6g — how do I choose?
t3.medium (burstable): good for dev/staging with unpredictable loads. r6g (memory-optimized Graviton): best price/performance for production — r6g.large gives 13GB memory at about the same cost as r5.large with 12GB. Avoid t-series in production for latency-sensitive workloads.
How do I handle cache stampede (thundering herd)?
When a hot cache key expires, hundreds of requests simultaneously miss and hammer the database. Solutions: use probabilistic early expiration (XFetch algorithm), cache locking (SET NX with short TTL to let one request rebuild), or background refresh (refresh before expiry using a background thread).
How do I monitor ElastiCache Redis?
Key CloudWatch metrics: CacheHitRate (should be >90%), CurrConnections (watch for connection leaks), DatabaseMemoryUsagePercentage (alert at 75%), EngineCPUUtilization (alert at 70%), SwapUsage (should be 0). Enable slow log in Redis config to identify slow commands.