Loading...

12-Hour Money-Back Guarantee

Read Amplification vs Write Amplification

Read Amplification vs Write Amplification

Read Amplification vs Write Amplification

3 Apr 20223 min read

The Hidden Cost of “Optimizations”

Many systems fail not because they’re slow, but because they do too much unnecessary work.

🧠 Definitions

🔹 Read Amplification

One logical read causes multiple physical reads

1 API read → 5 DB / Cache reads

🔹 Write Amplification

One logical write causes multiple physical writes

1 API write → 5 DB / Cache / Index writes

🧩 Why This Matters (Real Systems)

Even if:

  • Cache hit rate is high

  • DB is healthy

👉 Amplification silently:

  • Burns CPU

  • Increases latency

  • Kills tail latency (P99)

🧩 Part 1 — Read Amplification

❌ Problem Example: Chat App (Naive)

Scenario

  • Fetch conversation

  • For each message → fetch sender

❌ Code

async function getConversation(convId) {
  const messages = await db.getMessages(convId);

  for (const msg of messages) {
    msg.user = await db.getUser(msg.userId); // N reads
  }
  return messages;
}

Cost

1 conversation
+ 100 messages
= 101 DB reads ❌

✅ Solution A — Batch Reads

async function getConversation(convId) {
  const messages = await db.getMessages(convId);
  const userIds = [...new Set(messages.map(m => m.userId))];

  const users = await db.getUsers(userIds); // 1 read
  const userMap = Object.fromEntries(users.map(u => [u.id, u]));

  return messages.map(m => ({
    ...m,
    user: userMap[m.userId]
  }));
}

Why Better

✔ 101 reads → 2 reads
✔ Lower latency
✔ Predictable load

🧩 Solution B — Denormalization (Tradeoff)

// Message already stores username/avatar
{
  message: "Hi",
  username: "subhahu"
}

✔ Zero extra reads
❌ Higher write amplification

🧩 Part 2 — Write Amplification

❌ Problem Example: Like a Post

Naive Write Path

async function likePost(postId, userId) {
  await db.insertLike(postId, userId);
  await redis.del(`post:${postId}`);
  await redis.del(`user:${userId}:likes`);
  await searchIndex.update(postId);
  await analytics.log("like");
}

Cost

1 user action
→ 5 writes ❌

✅ Solution A — Async Writes (Eventual Consistency)

async function likePost(postId, userId) {
  await db.insertLike(postId, userId);
  queue.publish("post_liked", { postId, userId });
}

Workers:

queue.on("post_liked", async (e) => {
  await redis.del(`post:${e.postId}`);
  await searchIndex.update(e.postId);
});

Why Better

✔ Fast user response
✔ Load smoothing
❌ Eventual consistency

🧠 Read vs Write Amplification Tradeoff

Choice Read Amp Write Amp
Normalized 🔥 High 🟢 Low
Denormalized 🟢 Low 🔥 High
Index-heavy 🟢 🔥🔥
Caching 🟢 🔥

🔥 How This Connects to Previous Topics

  • Cache stampede → read amplification spike

  • Hot keys → read amplification concentration

  • TTL jitter → smooths read amplification

  • SWR → trades freshness for read efficiency