Caching with ElastiCache
Why reading from the database on every request is slow and expensive, how an in-memory cache solves it, the Valkey, Redis and Memcached engines, the ways to keep data fresh, and a hands-on where you build a cache and a leaderboard.
- Key idea
- Definition
- When to use
- Caution
In this article
In part eight we built a strong database. But imagine your store's home page shows the same 20 products to every visitor. Thousands of visitors means the database runs the same query thousands of times and returns the same answer.
The solution is simple: keep the answer somewhere fast, and go back to it instead of asking the database every time. That's a cache.
What a cache is
A cache is a temporary copy of data that takes time or effort to fetch. Memory is far faster than disk, so an answer usually arrives in under a millisecond. That gives you:
- Higher speed for the user.
- Less load on the database, so you can serve more visitors with a smaller one.
The key rule: a cache isn't the source of truth. If you lose all of it, your application gets a bit slower, but no important data is lost.
ElastiCache is a managed cache service: AWS handles the servers, patches and failover. Unlike RDS, though, you have to change your application's code, because the application decides what to store and when to read it.
Choosing an engine
| Valkey and Redis OSS | Memcached | |
|---|---|---|
| Data types | Strings, lists, sets, sorted sets and more | Simple values |
| Copies and failover | Read replicas, Multi-AZ and automatic failover | No copies in a cluster you design yourself |
| Persistence | Snapshots you can restore from | None; data lives only in memory |
| Scaling | Partitioned data with replicas | Partitioned across nodes, and multi-threaded |
| Good for | Most cases: caching, sessions, leaderboards | A simple cache where losing data doesn't hurt |
You have two ways to run it:
| Serverless | A cluster you design | |
|---|---|---|
| Setup | Little more than a name | You choose node types, partitions and replicas |
| Scaling | Automatic | You plan it yourself |
| Billing | Data size and operations | Per node, per hour |
| Good for | Irregular load, and the easiest start | Steady load and tight cost control |
How your application uses it
Lazy loading
This is the most common approach, also called cache-aside: your application asks the cache first, goes to the database if the answer isn't there, then stores the answer for next time.
In code, it looks roughly like this:
def get_product(product_id):
key = f"product:{product_id}"
cached = cache.get(key)
if cached:
return json.loads(cached)
product = db.fetch_product(product_id)
cache.set(key, json.dumps(product), ex=300) # Valid for 5 minutes
return product
- Pros: it stores only what's actually requested, and if the cache fails the application carries on from the database.
- Cons: the first request is slow, and data can be stale until it expires.
Write-through
Whenever you write to the database, you write to the cache as well.
- Pros: the cache is always current.
- Cons: an extra write every time, and the cache can fill with data nobody reads.
Many applications combine the two: lazy loading, plus updating the cache on writes for important data.
TTL and eviction
Each key has a lifetime (TTL) after which it disappears, so stale data doesn't stay forever. The right value depends on the data:
| Data | A sensible TTL | Why |
|---|---|---|
| Home page and lists | 30 to 60 seconds | Even a short TTL removes most of the load |
| Product details | Minutes | It's fine if it lags slightly |
| User session | The session length, such as 30 minutes | Renewed with activity |
| Final balance or stock | Don't cache it | It has to be accurate |
When memory fills up, the cache evicts some keys, usually the least used. If this happens a lot, your cache is too small.
More than a cache
Sessions: remember the table in part seven? When you keep sessions in ElastiCache, any server can read them, so users don't get signed out when their server disappears.
Leaderboards: a sorted set keeps items ordered by score and gives you the top entries instantly, instead of sorting millions of rows in the database.
Rate limiting: an INCR counter with a TTL counts each user's requests per minute and blocks anyone over the limit.
Security and monitoring
- Network: by default the cache is reachable only from inside your network, so let its security group accept traffic only from the servers' security group.
- Encryption: turn it on in transit and at rest; it's always on with Serverless.
- Access: use users and permissions or IAM authentication on Valkey and Redis, and SASL on Memcached.
In CloudWatch, watch these metrics:
| Metric | What it tells you |
|---|---|
CacheHitRate |
How many requests found their answer in the cache. If it's low, review what you store and your TTLs |
Evictions |
How many keys were pushed out by lack of memory |
| Memory usage | Whether you need a bigger cache |
Hands-on: a cache and a leaderboard
We'll use your server from part three. From CloudShell, create a security group that accepts traffic from the server's security group. Serverless uses ports 6379 and 6380:
aws ec2 create-security-group --group-name cache-sg \
--description "Cache access" --query GroupId --output text
aws ec2 authorize-security-group-ingress --group-id sg-CACHE-GROUP \
--protocol tcp --port 6379-6380 --source-group sg-SERVER-GROUP
Then create the cache using the ID you got back:
aws elasticache create-serverless-cache \
--serverless-cache-name series-cache \
--engine valkey \
--security-group-ids sg-CACHE-GROUP
aws elasticache describe-serverless-caches \
--serverless-cache-name series-cache \
--query "ServerlessCaches[0].[Status,Endpoint.Address]" --output table
When the status shows available, sign in to the server through EC2 Instance Connect and connect:
sudo dnf install -y valkey
valkey-cli -h YOUR-CACHE-ENDPOINT --tls -c -p 6379
Try a TTL:
SET product:1 "Keyboard"
GET product:1
SET offer:today "20% off" EX 30
TTL offer:today
Repeat TTL a few times and watch the countdown. After 30 seconds, GET offer:today returns (nil).
Now a leaderboard:
ZADD leaderboard 120 sara 300 omar 90 lina
ZINCRBY leaderboard 250 lina
ZREVRANGE leaderboard 0 2 WITHSCORES
You'll see lina in first place with 340 points, with no sorting on your side. Leave with exit.
aws elasticache delete-serverless-cache --serverless-cache-name series-cache
aws ec2 delete-security-group --group-id sg-CACHE-GROUP
Summary
- A cache is a fast, temporary copy, not the source of truth.
- Start with Valkey, and with Serverless if your load is irregular or you want an easy start.
- Lazy loading is the most common approach, and write-through keeps the cache current.
- The TTL sets how much staleness you accept, and some data shouldn't be cached at all.
- It also works for sessions, leaderboards and rate limiting.
Test yourself
Before you move on, try these questions to check you've got this part. Each one is a practical scenario, and the explanation appears once you answer.
Question 1 of 5
A store's home page runs the same query on RDS thousands of times a minute, and the database's CPU is high. The data changes only a few times an hour. What gives the fastest responses and takes the most load off the database?
Show the answer
Correct answer: C
The answer rarely changes, so the database doesn't need to work it out again each time. Memory answers in under a millisecond, and even a short TTL removes most of the load.
- A: The standby doesn't serve reads, so the load stays the same.
- B: This buys capacity, but the same query still runs every time.
- D: Redshift is for large reports, not for answering a page quickly.
Review: What a cache is in this part, and Caching strategies in the AWS docs.
Question 2 of 5
A mobile game shows the top 100 players live. The leaderboard must keep working if a cache node fails, with automatic failover. Which option fits?
Show the answer
Correct answer: A
A sorted set keeps players ordered by score and returns the top entries instantly, and Valkey supports replicas with automatic failover.
- B: Memcached stores simple values, and it has no copies in a cluster you design.
- C: Sorting millions of rows on every request puts heavy load on the database.
- D: S3 isn't built for a file that changes constantly and must be read live.
Review: Choosing an engine in this part, and Comparing engines in the AWS docs.
Question 3 of 5
A store uses lazy loading with a one-hour TTL, so customers can see an old price for up to an hour after it changes. Prices must now update within a minute, while keeping the benefit of the cache. What should the team do?
Show the answer
Correct answer: D
Lazy loading alone leaves data stale until it expires. Handling the cache on each write keeps it current, and a shorter TTL limits the damage if a write is ever missed.
- A: This solves staleness, but brings all the load back to the database.
- B: This makes the problem worse.
- C: The stale value comes from the cache, not from the database.
Review: Write-through in this part, and Caching strategies in the AWS docs.
Question 4 of 5
An application fills its cache at startup, giving every key the same TTL. At the end of that period, the database gets a sharp spike in load, and the pattern repeats. What's the simplest fix?
Show the answer
Correct answer: B
When keys expire together, requests for all of them go to the database at the same moment. Randomizing the TTL spreads those expiries over time.
- A: The problem is in the database at the moment of expiry, not in the cache's size.
- C: Keys would never refresh, so stale data would stay.
- D: This protects the cache from failures, and the keys still expire together.
Review: TTL and eviction in this part, and Best practices for TTLs and jitter in the AWS docs.
Question 5 of 5
A team sees the Evictions metric rising and CacheHitRate falling on its cache. Which two actions address this? (Choose two.)
Show the answer
Correct answer: A + C
Evictions mean the cache is full and is pushing keys out, which turns into misses. Either make room with more memory, or stop filling it with what nobody reads.
- B: This helps the database, but it doesn't change the cache's memory.
- D: Encryption has nothing to do with evictions, and you'd lose its protection.
- E: S3 is far slower, and this removes the point of having a cache.
Review: Security and monitoring in this part, and ElastiCache for e-commerce in the AWS docs.
You now know RDS, Aurora and ElastiCache, and AWS has many more. In part ten we answer the question that confuses everyone: which database should you choose?