Skip to content

Expert · Performance and reliability · topic 40 of 54 · 30 min

Caching and Redis

Explanation

A cache keeps a copy of data that is slow to get, so the next request can be answered fast. The common pattern is cache-aside: look in the cache, and only on a miss ask the database and save the answer with a time limit (TTL). Redis is a very fast in-memory store used for this. The hard part is making sure old (stale) data does not stay too long.

Why cache at all

Some answers are slow or costly to make: a database query that joins five tables, a call to another company's API, a page that needs a lot of work to build. If thousands of people ask the same question every minute, doing that work every time wastes time and money.

A cache is a place that keeps a ready copy of an answer for a short time. A cache hit means the answer was found in the cache. A cache miss means it was not, so the app does the slow work and usually saves the answer for next time.

Think of it like this: A kirana shop keeps the fast-selling items, like milk, bread and biscuits, on the counter. Everything else is in the godown at the back. Most customers are served from the counter in seconds. The counter is the cache; the godown is the database. The shopkeeper must still check the milk on the counter is not past its date: that is the TTL.

Where caches live

PlaceWhat it keepsExample
BrowserFiles and responses the user already downloadedLogo and CSS are not downloaded again on every page
CDNCopies of files on servers close to usersImages served from a Mumbai edge server to users in Pune
Server memoryValues inside one running app processA Map of settings loaded at start-up
Redis (shared cache)Values shared by all your app serversProduct details used by 10 servers at once

Cache-aside, step by step

  1. A request asks for product 42.
  2. The app builds a key, like product:42, and looks in the cache.
  3. Hit: return the cached copy. Done, and the database was not touched.
  4. Miss: read product 42 from the database.
  5. Save it in the cache with a TTL, for example 5 minutes.
  6. Return it. The next request within 5 minutes will be a hit.
  7. When the product changes, delete its cache key so the next read gets fresh data.
javascript
async function getProduct(id) {
  const key = 'product:' + id;
  const cached = await redis.get(key);              // look in the cache
  if (cached) return JSON.parse(cached);            // hit
  const product = await db.findProduct(id);         // miss: ask the database
  await redis.set(key, JSON.stringify(product), { EX: 300 }); // keep for 300 seconds
  return product;
}

async function updatePrice(id, price) {
  await db.updatePrice(id, price);
  await redis.del('product:' + id);                 // invalidate the old copy
}
Cache-aside with the node-redis client (runs on a server with Node.js and Redis).

TTL and invalidation

TTL means time to live: how long a cached value may be used before it expires and is thrown away. A short TTL (seconds) keeps data fresh but gives fewer hits. A long TTL (hours) gives many hits but data can be old.

Invalidation means removing or updating a cached value when the real data changes. The simple, reliable way is: write to the database first, then delete the cache key. The next read will load fresh data. There is a famous saying among programmers that cache invalidation is one of the hardest problems, because it is easy to forget one place that changes the data.

Careful: Stale data is data in the cache that is older than the truth. It is fine for a list of blog posts, but dangerous for a bank balance, seat availability on a train, or stock in a flash sale. Do not cache these, or use very short TTLs and always check the database before taking money.

Redis in a few lines

Redis is an in-memory key-value store. In-memory means it keeps data in RAM, so reads take well under a millisecond. Key-value means you save a value under a name (the key) and get it back by that name. It also has counters, lists, sets and expiry built in. Valkey is an open-source fork of Redis (a copy that continues as its own project) with the same commands.

output
127.0.0.1:6379> SET product:42 "Atta 5kg, Rs 260" EX 300
OK
127.0.0.1:6379> GET product:42
"Atta 5kg, Rs 260"
127.0.0.1:6379> TTL product:42
(integer) 297
127.0.0.1:6379> INCR visits:home
(integer) 1
127.0.0.1:6379> DEL product:42
(integer) 1
A short redis-cli session: SET with EX (expire in seconds), GET, TTL, INCR (add one) and DEL.

HTTP caching is the same idea between your server and browsers or CDNs. The Cache-Control response header says who may keep a copy and for how long, for example Cache-Control: public, max-age=86400 for a logo, or Cache-Control: no-store for a bank statement. An ETag header is a version tag: the browser asks "has it changed since this tag?" and the server can answer 304 Not Modified with no body.

Tip: Measure before you cache. Find the slow, frequently repeated reads first (with logs or metrics), cache those, and keep the rest simple.

Watch the lesson

Caching and Redis · 1/7

Cache = a ready copy

Some answers are slow to make. A cache keeps a ready copy so the next request is fast.

Prefer a video by a teacher? Find free videos on “caching explained redis cache aside pattern beginners” on YouTube ↗

In real life: A food delivery menu at lunch time

At 1 pm, thousands of people open the same popular restaurant's menu. Without a cache, each visit runs the same database query for the menu, prices and photos. With cache-aside, the first request loads the menu from the database and saves it in Redis for 60 seconds; the next thousands of requests are served from Redis. When the restaurant marks "Paneer Tikka" as sold out, the app deletes the menu key so the next visitor sees the change. The order total, though, is always calculated from the database, never from the cache.

Practice (0/3 done)

Task 1 · Code · Javascript

Build a TtlCache class. The constructor takes now, a function that returns the current time in milliseconds. set(key, value, ttlMs) stores a value that expires ttlMs after now(). get(key) returns the value, or undefined if it is missing or expired (expired means now() >= the expiry time).

Task 2 · Code · Javascript

Write getProduct(id) using cache-aside. Use the Map called cache with keys like "product:7". On a hit return the cached product. On a miss call await db.findProduct(id), save the result in the cache, and return it. The tests check that a second call for the same id does not hit the database.

Task 3 · Practice

For a college results website on result day, decide what to cache, where, and for how long. Also say what you will NOT cache and why.

Quick quiz

Question 1

1.What is a cache miss?

Question 2

2.In cache-aside, what happens right after a miss?

Question 3

3.What does SET key value EX 60 do in Redis?

Question 4

4.A product's price changed in the database. What should you do with its cache key?

Question 5

5.Which value is the WORST choice to serve from a long-lived cache?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Keeping secrets safe