Caching to Reduce Database Load
Key concepts
ElastiCache cost vs RDS savings
DAX for DynamoDB
Cache hit ratio optimization
TTL strategies
Read-through vs cache-aside
Overview
Implementing caching is one of the most effective strategies for reducing database costs in AWS. By serving frequently accessed data from an in-memory cache instead of hitting the database, you can dramatically reduce read capacity requirements, lower latency, and avoid expensive database scaling. This topic covers ElastiCache, DynamoDB Accelerator (DAX), cache optimization strategies, and how to calculate the cost-benefit of caching implementations.
Caching Economics
Caching creates a fundamental cost trade-off: you pay for cache infrastructure (ElastiCache nodes, DAX clusters) but save significantly on database costs (RDS instance sizing, DynamoDB read capacity). The key insight is that cache costs are typically 10-20% of the database costs they replace. A well-implemented caching layer with 80%+ hit ratio can reduce database load by 80%, often allowing you to downsize database instances by one or two tiers.
The exam frequently presents scenarios involving high read traffic, database performance issues, or cost optimization requirements. When you see 'reduce database load,' 'improve read performance,' or 'optimize costs for read-heavy workloads,' caching is almost always part of the correct answer. Know when to use ElastiCache Redis vs Memcached and when DAX is the appropriate solution for DynamoDB.
Key Concepts
ElastiCache Cost vs RDS Savings

ElastiCache Pricing Model
Purpose: Fully managed in-memory caching service that reduces database load and improves application performance
Pricing Components:
- Node Hours: Per-hour charge based on node type (cache.t3.micro to cache.r6g.16xlarge)
- Data Transfer: Data transferred out of ElastiCache (in-region transfer is free)
- Backup Storage: Automated backups beyond free allocation (Redis only)
Pricing Examples (US-East-1, On-Demand):
- cache.t3.micro: $0.017/hour (~$12.41/month)
- cache.r6g.large: $0.136/hour (~$99.28/month)
- cache.r6g.xlarge: $0.272/hour (~$198.56/month)
- cache.r6g.2xlarge: $0.544/hour (~$397.12/month)
Reserved Node Savings:
- 1-year reservation: Up to 31% savings
- 3-year reservation: Up to 55% savings
Database Cost Reduction Analysis
Cost Comparison Example:
Before Caching (RDS MySQL):
- db.r6g.2xlarge: $0.96/hour = ~$700/month
- Multi-AZ: $1.92/hour = ~$1,400/month
- Read Replicas (3x): Additional ~$2,100/month
- Total: ~$3,500/month
After Caching (80% cache hit ratio):
- ElastiCache (cache.r6g.large, 2 nodes): ~$200/month
- RDS downsized to db.r6g.large: $0.48/hour = ~$350/month
- Multi-AZ: ~$700/month
- Read Replicas reduced to 1: ~$350/month
- Total: ~$1,600/month
Monthly Savings: ~$1,900 (54% reduction)
Key Insight: Cache node cost (~$200) enables RDS savings (~$2,100) - a 10:1 return on cache investment
When Caching Pays Off
High ROI Scenarios:
- Read-heavy workloads (70%+ reads)
- Frequently accessed data (hot spots)
- Data that changes infrequently
- Complex database queries that can be cached
- High database instance costs
Low ROI Scenarios:
- Write-heavy workloads
- Data that changes every request
- Highly random access patterns (no hot spots)
- Very small databases already on minimum instance sizes
Break-Even Analysis:
- If cache hit ratio < 50%, savings may not justify cache cost
- If database is already minimal size, no room to downsize
- Consider operational complexity costs
DAX for DynamoDB

DynamoDB Accelerator (DAX) Overview
Purpose: Fully managed, highly available, in-memory cache specifically designed for DynamoDB
Key Characteristics:
- Microsecond response times (vs milliseconds for DynamoDB)
- API-compatible with DynamoDB (minimal code changes)
- Write-through cache (writes go to both cache and DynamoDB)
- Handles cache invalidation automatically
- Cluster-based deployment (3+ nodes for production)
DAX vs ElastiCache for DynamoDB:
- DAX: Purpose-built, API-compatible, automatic cache management
- ElastiCache: More flexible, requires application-level cache logic
- DAX: Best for simple caching of DynamoDB reads
- ElastiCache: Best for complex caching patterns, session storage
DAX Pricing Model
Pricing Components:
- Node Hours: Per-hour charge based on node type
- Data Transfer: Free within same AZ, charges for cross-AZ
- No separate charge for storage (in-memory only)
Pricing Examples (US-East-1, On-Demand):
- dax.t3.small: $0.04/hour (~$29.20/month)
- dax.r5.large: $0.269/hour (~$196.37/month)
- dax.r5.xlarge: $0.538/hour (~$392.74/month)
- dax.r5.2xlarge: $1.076/hour (~$785.48/month)
Minimum Production Setup (3 nodes):
- 3x dax.r5.large: ~$589/month
Reserved Capacity Savings:
- 1-year: Up to 35% savings
- 3-year: Up to 55% savings
DAX Cost Savings Calculation
Scenario: High-traffic application with 100,000 reads/second
Without DAX (On-Demand DynamoDB):
- Eventually consistent reads: 50,000 RRUs (100K reads / 2)
- Cost: 50,000 * $0.00013/RRU/hour = $6.50/hour
- Monthly: ~$4,745
With DAX (90% cache hit ratio):
- DAX cluster (3x dax.r5.large): ~$589/month
- DynamoDB reads reduced to 10,000/second
- DynamoDB cost: 5,000 RRUs = ~$475/month
- Total: ~$1,064/month
Monthly Savings: ~$3,681 (78% reduction)
Break-Even Point: DAX pays off when read traffic justifies cluster cost
- Minimum viable: ~15,000+ reads/second for r5.large cluster
Cache Hit Ratio Optimization

Understanding Cache Hit Ratio
Definition: The percentage of read requests served from cache vs total read requests
Formula: Cache Hit Ratio = (Cache Hits) / (Cache Hits + Cache Misses) * 100
Impact on Cost Savings: | Hit Ratio | DB Load Reduction | Typical Savings | |-----------|------------------|-----------------| | 50% | 50% | 20-30% cost reduction | | 70% | 70% | 40-50% cost reduction | | 80% | 80% | 50-60% cost reduction | | 90% | 90% | 60-75% cost reduction | | 95% | 95% | 70-80% cost reduction |
Industry Benchmarks:
- Minimum acceptable: 70%
- Good: 80-90%
- Excellent: 90-95%
- World-class: 95%+
Strategies to Improve Hit Ratio
1. Cache Warming:
- Pre-populate cache before traffic spikes
- Load frequently accessed data at startup
- Use scheduled jobs to refresh popular items
2. Appropriate TTL Settings:
- Too short: Low hit ratio, frequent DB calls
- Too long: Stale data, memory pressure
- Tune based on data change frequency
3. Key Design:
- Use consistent, predictable key patterns
- Include version numbers for cache invalidation
- Avoid overly granular keys (reduces hit probability)
4. Right-Size Cache Memory:
- Monitor eviction rates
- If evictions high, increase memory or reduce TTL
- Use CloudWatch MemoryUsage and Evictions metrics
5. Data Structure Optimization:
- Cache aggregated/computed results (not raw data)
- Use appropriate data structures (strings, hashes, sorted sets)
- Compress large values to fit more in memory
Monitoring Cache Performance
Key CloudWatch Metrics for ElastiCache:
Hit Ratio Metrics:
CacheHits: Number of successful cache readsCacheMisses: Number of cache missesCacheHitRate: Percentage of hits (calculated)
Capacity Metrics:
BytesUsedForCache: Memory currently usedEvictions: Number of items evicted due to memory pressureCurrConnections: Current client connectionsNewConnections: New connections per second
Health Metrics:
CPUUtilization: Node CPU usageNetworkBytesIn/Out: Network throughputReplicationLag: Replica sync delay (Redis)
Alarm Thresholds:
- Cache hit ratio < 70%: Investigate access patterns
- Evictions > 0 sustained: Increase memory
- CPU > 65%: Consider scaling
TTL Strategies

TTL (Time to Live) Fundamentals
Purpose: Automatically expire cached data to ensure freshness and manage memory
How TTL Works:
- Set expiration time when caching data
- Cache automatically removes expired items
- Next request triggers cache miss and fresh data fetch
TTL Trade-offs:
- Short TTL (seconds to minutes): Fresh data, lower hit ratio, more DB load
- Long TTL (hours to days): Higher hit ratio, potentially stale data
- No TTL: Maximum hit ratio, manual invalidation required
Cost Impact:
- Shorter TTL = more database calls = higher database costs
- Longer TTL = fewer database calls = lower database costs
- Balance freshness requirements with cost optimization
TTL Strategy by Data Type
Static Configuration Data (Hours to Days):
- Application settings
- Feature flags
- Reference data (countries, categories)
- TTL: 1-24 hours
User Profile Data (Minutes to Hours):
- User preferences
- Account information
- Permissions/roles
- TTL: 5-60 minutes
Session Data (Minutes):
- Shopping carts
- Authentication tokens
- Form state
- TTL: 15-30 minutes
Real-Time Data (Seconds):
- Stock prices
- Live scores
- Inventory counts
- TTL: 5-60 seconds
Computed Results (Variable):
- Search results
- Aggregations
- Reports
- TTL: Based on underlying data change frequency
Advanced TTL Patterns
Staggered TTL:
- Add random jitter to prevent thundering herd
- Example: Base TTL 60s + random(0-30s)
- Prevents all caches expiring simultaneously
Sliding TTL:
- Reset TTL on each access
- Keeps hot data in cache longer
- Reduces DB load for popular items
Adaptive TTL:
- Adjust TTL based on access patterns
- Popular items: Longer TTL
- Rarely accessed: Shorter TTL
- Requires custom implementation
Cache Tags:
- Group related cache entries
- Invalidate entire groups at once
- Example: All user-123 related caches
Read-Through vs Cache-Aside Patterns

Cache-Aside (Lazy Loading)
How It Works:
- Application checks cache first
- If cache hit: Return cached data
- If cache miss: Query database
- Store result in cache
- Return data to caller
Advantages:
- Only requested data is cached
- Cache failures don't break application
- Simple to implement
- Works with any database
Disadvantages:
- First request always hits database
- Application manages cache logic
- Potential for stale data
- Cache misses add latency (cache check + DB call)
Best For:
- General-purpose caching
- Applications with unpredictable access patterns
- When database can handle cache miss spikes
Read-Through Cache
How It Works:
- Application always reads from cache
- Cache automatically fetches from database on miss
- Cache manages data loading logic
- Application code simplified
Advantages:
- Simpler application code
- Consistent cache population logic
- Cache handles database interaction
- Better abstraction
Disadvantages:
- Cache becomes critical path
- More complex cache infrastructure
- Limited flexibility in fetch logic
- May cache unnecessary data
Best For:
- Standardized data access patterns
- When cache infrastructure can handle load
- Reducing application complexity
AWS Implementation: DAX is a read-through cache for DynamoDB
Write-Through Cache
How It Works:
- Application writes to cache
- Cache synchronously writes to database
- Both updated before confirming write
- Read always from cache
Advantages:
- Cache always has latest data
- No stale data issues
- Simplified read path
- Consistent data
Disadvantages:
- Write latency increased
- Cache failures can block writes
- All data cached (even if never read)
- Higher cache memory usage
Best For:
- Read-heavy workloads
- Data that must always be fresh
- When write latency is acceptable
AWS Implementation: DAX uses write-through for DynamoDB
Write-Behind (Write-Back) Cache
How It Works:
- Application writes to cache only
- Cache acknowledges immediately
- Cache asynchronously writes to database
- Batches writes for efficiency
Advantages:
- Fastest write performance
- Reduced database write load
- Batching improves efficiency
- Lower database costs
Disadvantages:
- Risk of data loss if cache fails
- Eventual consistency
- Complex failure handling
- Not suitable for critical data
Best For:
- Write-heavy workloads
- When some data loss is acceptable
- Analytics and logging
- Non-critical updates
Caching Pattern Selection Guide
| Pattern | Data Freshness | Performance | Complexity | Best Use Case |
|---|---|---|---|---|
| Cache-Aside | Depends on TTL | Good (miss penalty) | Low | General-purpose, flexible requirements |
| Read-Through | Depends on TTL | Good (miss penalty) | Medium | Standardized read patterns |
| Write-Through | Always fresh | Good reads, slower writes | Medium | Read-heavy, fresh data required |
| Write-Behind | Eventually consistent | Fastest writes | High | Write-heavy, loss acceptable |
Best Practices
-
Start with Cache-Aside for Flexibility: Begin with the cache-aside pattern as it is simplest to implement and provides the most control over caching behavior
-
Target 80%+ Cache Hit Ratio: Design your caching strategy to achieve at least 80% cache hit ratio before investing in caching infrastructure
-
Use DAX for DynamoDB-Only Workloads: When caching only DynamoDB data, DAX provides the simplest implementation with automatic cache management
-
Set Appropriate TTLs by Data Type: Match TTL to data change frequency - static data can have long TTLs, frequently changing data needs shorter TTLs
-
Monitor and Alert on Cache Metrics: Set up CloudWatch alarms for cache hit ratio, evictions, and memory usage to detect issues early
-
Implement Cache Warming for Predictable Traffic: Pre-populate caches before known traffic spikes to avoid cold cache penalties
-
Use Multi-AZ for Production Caches: Deploy ElastiCache clusters across multiple AZs for high availability, especially for session stores
-
Right-Size Cache Nodes: Monitor memory usage and eviction rates to find the optimal node size - too small causes evictions, too large wastes money
Common Exam Scenarios
Caching Exam Scenario Decision Guide
| Scenario | Recommended Solution | Key Reasoning |
|---|---|---|
| DynamoDB table with very high read traffic causing throttling | Implement DAX cluster | DAX is purpose-built for DynamoDB, provides microsecond latency, reduces RCU consumption |
| RDS database experiencing high CPU due to repeated read queries | Add ElastiCache Redis cluster | Cache frequently accessed queries, reduce database load, improve response time |
| Need to store user session data with sub-millisecond latency | ElastiCache Redis with Multi-AZ | In-memory storage for sessions, Redis persistence for durability, Multi-AZ for HA |
| E-commerce product catalog with millions of reads per day | ElastiCache with cache-aside pattern | Cache product data, invalidate on updates, handle high read volume cost-effectively |
| Real-time leaderboard requiring sorted data | ElastiCache Redis sorted sets | Redis sorted sets provide efficient leaderboard operations with automatic ranking |
| Reduce costs for read-heavy DynamoDB table with predictable access patterns | DAX with reserved capacity | DAX reduces RCU costs, reserved capacity provides additional savings |
| Application experiencing cold start latency after deployments | Implement cache warming during deployment | Pre-populate cache to avoid miss penalties, maintain consistent performance |
| Need to cache complex join queries from Aurora | ElastiCache Redis with query result caching | Cache computed results of expensive queries, significant database offload |
Common Pitfalls
Caching Without Measuring Hit Ratio
Implementing caching without monitoring cache hit ratio can lead to wasted infrastructure costs. If your cache hit ratio is below 70%, you may be paying for cache infrastructure without meaningful database savings. Always implement CloudWatch monitoring for cache hits and misses before assuming cost benefits.
Using ElastiCache When DAX is Better
For DynamoDB-only caching needs, using ElastiCache instead of DAX adds unnecessary complexity. DAX is API-compatible with DynamoDB, handles cache invalidation automatically, and requires minimal code changes. Only use ElastiCache for DynamoDB when you need features DAX doesn't provide (like sorted sets or pub/sub).
Setting TTL Too Short
Overly aggressive TTLs (seconds) can result in cache hit ratios below 50%, negating the benefits of caching. If your data doesn't change frequently, use longer TTLs. A 5-minute TTL often provides 10x better hit ratio than a 30-second TTL with minimal freshness impact for most use cases.
Not Planning for Cache Failures
Applications must handle cache failures gracefully. If your application cannot function when the cache is unavailable, you have created a single point of failure. Implement circuit breakers and fallback to database during cache outages. Never let cache failures cascade to application failures.
Caching User-Specific Data Without Proper Keys
Caching personalized data without including user identifiers in cache keys can leak data between users - a serious security vulnerability. Always include user ID, tenant ID, or session ID in cache keys for user-specific data. Example: user:12345:preferences not just preferences.
Related Services
Quick Reference
Caching Service Comparison
| Feature | ElastiCache Redis | ElastiCache Memcached | DAX |
|---|---|---|---|
| Purpose | General caching | Simple caching | DynamoDB caching |
| Data Structures | Rich (strings, hashes, lists, sets, sorted sets) | Key-value only | Key-value only |
| Persistence | Yes (RDB, AOF) | No | No |
| Replication | Yes | No | Yes |
| Multi-AZ | Yes | No | Yes |
| Clustering | Yes | Yes | Yes |
| Max Nodes | 500 (cluster mode) | 40 | 11 |
| API | Redis commands | Memcached protocol | DynamoDB API |
| Use Case | Sessions, leaderboards, complex caching | Simple caching | DynamoDB read acceleration |
ElastiCache Pricing Quick Reference (US-East-1, On-Demand)
| Node Type | Memory | Price/Hour | Price/Month |
|---|---|---|---|
| cache.t3.micro | 0.5 GB | $0.017 | ~$12 |
| cache.t3.small | 1.37 GB | $0.034 | ~$25 |
| cache.t3.medium | 3.09 GB | $0.068 | ~$50 |
| cache.r6g.large | 13.07 GB | $0.136 | ~$99 |
| cache.r6g.xlarge | 26.32 GB | $0.272 | ~$199 |
| cache.r6g.2xlarge | 52.82 GB | $0.544 | ~$397 |
DAX Pricing Quick Reference (US-East-1, On-Demand)
| Node Type | Memory | Price/Hour | Price/Month |
|---|---|---|---|
| dax.t3.small | 2 GB | $0.04 | ~$29 |
| dax.t3.medium | 4 GB | $0.08 | ~$58 |
| dax.r5.large | 16 GB | $0.269 | ~$196 |
| dax.r5.xlarge | 32 GB | $0.538 | ~$393 |
| dax.r5.2xlarge | 64 GB | $1.076 | ~$785 |
Cache Hit Ratio Impact Calculator
| Starting DB Cost | Cache Cost | Hit Ratio | DB Reduction | Net Savings |
|---|---|---|---|---|
| $1,000/month | $100/month | 80% | $800/month | $700/month (70%) |
| $1,000/month | $100/month | 90% | $900/month | $800/month (80%) |
| $5,000/month | $300/month | 80% | $4,000/month | $3,700/month (74%) |
| $5,000/month | $300/month | 90% | $4,500/month | $4,200/month (84%) |
Essential CLI Commands
# Create a Redis replication group with Multi-AZ
aws elasticache create-replication-group \
--replication-group-id my-redis-cluster \
--replication-group-description "Production Redis cache" \
--engine redis \
--cache-node-type cache.r6g.large \
--num-cache-clusters 3 \
--automatic-failover-enabled \
--multi-az-enabled \
--cache-subnet-group-name my-subnet-group \
--security-group-ids sg-12345678
# List ElastiCache clusters
aws elasticache describe-cache-clusters \
--query 'CacheClusters[*].[CacheClusterId,CacheNodeType,Engine,CacheClusterStatus]'# Create DAX cluster
aws dax create-cluster \
--cluster-name my-dax-cluster \
--node-type dax.r5.large \
--replication-factor 3 \
--iam-role-arn arn:aws:iam::123456789012:role/DAXRole \
--subnet-group-name my-dax-subnet-group \
--security-group-ids sg-12345678
# Describe DAX clusters
aws dax describe-clusters \
--query 'Clusters[*].[ClusterName,NodeType,TotalNodes,Status]'# Get ElastiCache hit/miss metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/ElastiCache \
--metric-name CacheHits \
--dimensions Name=CacheClusterId,Value=my-cluster \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Sum
# Get cache miss count
aws cloudwatch get-metric-statistics \
--namespace AWS/ElastiCache \
--metric-name CacheMisses \
--dimensions Name=CacheClusterId,Value=my-cluster \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Sum
# Get evictions (indicates memory pressure)
aws cloudwatch get-metric-statistics \
--namespace AWS/ElastiCache \
--metric-name Evictions \
--dimensions Name=CacheClusterId,Value=my-cluster \
--start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 3600 \
--statistics Sum# List available reserved cache node offerings
aws elasticache describe-reserved-cache-nodes-offerings \
--cache-node-type cache.r6g.large \
--duration 31536000 \
--product-description "redis" \
--query 'ReservedCacheNodesOfferings[*].[OfferingType,Duration,FixedPrice,RecurringCharges]'
# Purchase reserved cache node
aws elasticache purchase-reserved-cache-nodes-offering \
--reserved-cache-nodes-offering-id <offering-id> \
--cache-node-count 3
# List current reserved nodes
aws elasticache describe-reserved-cache-nodes \
--query 'ReservedCacheNodes[*].[ReservedCacheNodeId,CacheNodeType,Duration,State]'TTL Strategy Quick Reference
| Data Type | Recommended TTL | Rationale |
|---|---|---|
| Static configuration | 1-24 hours | Rarely changes, high hit ratio |
| User profiles | 5-60 minutes | Changes occasionally |
| Session data | 15-30 minutes | Security, cleanup |
| Product catalog | 5-30 minutes | Balance freshness and performance |
| Search results | 1-5 minutes | Acceptable staleness |
| Real-time prices | 5-60 seconds | Accuracy important |
| Computed aggregations | Based on source data | Invalidate on change |
Test Your Knowledge
A company has a DynamoDB table serving 50,000 read requests per second. They want to reduce costs while maintaining sub-millisecond read latency. What is the MOST cost-effective solution?
An application uses ElastiCache Redis for caching database queries. CloudWatch shows 45% cache hit ratio and high eviction rates. What should the solutions architect do FIRST to optimize cache performance?
A solutions architect is designing a caching strategy for an e-commerce application. Product catalog data changes daily, but product prices change every few hours during sales. Which TTL strategy is MOST appropriate?
A company runs a session store on ElastiCache Redis. They want to ensure zero session loss during infrastructure failures. Which configuration provides the HIGHEST durability?