◐ ai-teacher

⚡ Streaming & Kafka · Chapter 4

Choosing a Partition Key

The partition key decides two things at once: what stays ordered, and what gets overloaded.

Study this lesson free in the app →

The basics

A partition is one ordered slice of a Kafka topic, and the partition key is the field (like ride_id) whose hash decides which slice each event goes to.

The problem it solves: one machine can't handle every ride event in India at peak, so a topic is split into many partitions spread across brokers and read in parallel. But splitting has a price: Kafka only keeps events in order within one partition. The key is how you choose which events must stay together — and that same choice decides whether load spreads evenly or piles onto one machine.

The mental model: billing counters at a stadium. Send people to counters by ticket number and every counter is busy, but a family gets split up. Send them by surname initial and the "S" counter has a 40-minute queue while "Q" is empty. Pick the key that keeps together what must stay in order, and has enough distinct values to spread the crowd.

Words you must know 5

Partition key
the field hashed to choose a partition: hash(key) % partitions.
Ordering guarantee
same key → same partition → events stay in order.
Cardinality
how many distinct key values exist; 5 keys can't fill 50 partitions.
Hotspot / skew
one key (or a few) carrying far more traffic than the rest.
Salting
splitting a hot key into sub-keys to spread it, at the cost of its ordering.

Explained

Pick ride_id and every event of one ride stays in order, spread evenly across the cluster.

Pick city and Bangalore melts one broker while Shillong idles.

Pick event_timestamp and everything arriving this second lands on the same partition — you have built a queue with extra steps.

Billing counters at a stadium

Twenty counters, one crowd.

Key by surname initial — the S counter has a 40-minute queue, the Q counter reads a newspaper. That is skew.

Key by ticket number — even spread, every counter busy. But a family arriving together gets split across counters, so nothing keeps them in sequence.

Key by arrival minute — everyone in the same minute goes to one counter. Perfectly ordered, perfectly useless.

The trade is always the same: ordering pulls related events together, throughput pushes them apart.

How the key becomes a partition

partition = hash(key) % partition_count

Consequences that follow directly from that formula:

So: choose the key by asking "what must stay in order?" then verify "is it high-cardinality and evenly distributed?"

Good vs bad keys for RideFlow

stream                key            why
────────────────────────────────────────────────────────────
ride_events           ride_id        all events of one ride ordered;
                                     millions of ids = even spread

driver_location       driver_id      per-driver path stays sequential;
                                     high cardinality

payments              ride_id        charge/refund order preserved

✗ city                              10 keys, Bangalore = hotspot
✗ event_timestamp                   all of 'now' → one partition
✗ country                           1 key = 1 partition = no scale

When the natural key is skewed anyway

Sometimes reality is skewed — one merchant is 30% of transactions, one city is half the rides. Standard mitigations:

  1. Salting — key becomes merchant_id:bucket where bucket is 0..N. Spreads the hot key across N partitions, at the cost of ordering within that merchant (you now need a merge/sort downstream)
  2. Two-tier routing — hot keys get a dedicated topic with more partitions; everything else shares the default
  3. Reduce the event, not the key — aggregate location pings client-side to 1/second instead of 1/200ms
  4. Re-key at the boundary — ingest by ride_id, then re-partition by city only in the analytical layer, where skew costs a slow task rather than a dropped ping

Say salting in an interview and follow it immediately with "which breaks per-key ordering, so I'd only do it where ordering isn't required". The caveat is what makes it a senior answer.

Watch keys land in partitions

Same key, same partition — every time. That is the whole ordering guarantee: rider_7's events can never be split across partitions, so they stay in order. (Kafka uses murmur2, not a sum of characters, but the shape is the same: hash, then modulo the partition count.)

Now add partitions — and watch keys move

Same keys, 6 partitions instead of 4: most keys land somewhere new. A consumer that relied on per-key order across the change can now see events out of order. Decide the partition count up front.

Ordering pulls together. Throughput pushes apart. The key is where you settle the fight.

High cardinality, evenly spread, and matching the thing that must stay in order.

Words to know

Partition keyThe field hashed to decide which partition an event lands in.
HotspotOne partition receiving disproportionate traffic, limiting the whole system.
Data skewUneven distribution of records across partitions or tasks.
CardinalityThe number of distinct values a field can take.
SaltingAppending a bucket suffix to a hot key to spread it across partitions.
Re-keyingRe-partitioning a stream by a different field, usually via a shuffle.

In two minutes

Test yourself

1. Why is event_timestamp a poor partition key?

  1. Timestamps cannot be hashed
  2. All events from the same instant collide on one partition
  3. It breaks retention
  4. Consumers cannot read it
Show the answer

All events from the same instant collide on one partition — It creates a moving hotspot: whichever partition owns 'now' takes the full write load while others idle.

2. You need every status change of one ride processed in order. Key by…

  1. city
  2. ride_id
  3. driver_id
  4. no key (round robin)
Show the answer

ride_id — Same key → same partition → guaranteed relative order, and ride_id is high-cardinality so the spread stays even.

3. One merchant is 30% of your payment volume. Best first mitigation?

  1. Add more consumers
  2. Salt that key into sub-buckets
  3. Increase retention
  4. Switch to round-robin for everything
Show the answer

Salt that key into sub-buckets — More consumers cannot help — a partition is read by one member. Salting spreads the hot key, at the cost of per-merchant ordering.

4. You increase partitions from 6 to 12. What breaks?

  1. Retention resets
  2. hash(key) % n changes, so a key's old and new events may sit in different partitions
  3. Consumers lose their group
  4. Nothing at all
Show the answer

hash(key) % n changes, so a key's old and new events may sit in different partitions — The modulo changes the mapping. During and after the change, per-key ordering across the boundary is not guaranteed — plan it like a migration.

5. What happens to events produced with no key (null key)?

  1. They're spread across partitions (in sticky batches), with no ordering between them
  2. They all go to partition 0
  3. The producer throws an error
  4. They're stored but never delivered
Show the answer

They're spread across partitions (in sticky batches), with no ordering between them — With no key there's nothing to hash, so the producer spreads events for throughput (modern clients fill a batch for one partition, then switch). Maximum spread, zero ordering — fine for independent logs, wrong for state changes.

6. A driver app sends a GPS ping every 2 seconds and the ETA service must process each driver's pings in order. Key by…

  1. city
  2. event_timestamp
  3. ping_id (unique per ping)
  4. driver_id
Show the answer

driver_id — Order matters per driver, and there are lakhs of drivers, so driver_id is both correct and evenly spread. ping_id spreads perfectly but scatters one driver's pings; city and timestamp create hotspots.

There are 8 questions, 3 puzzles and 5 recall cards for this lesson in the app — the recall cards come back on later days, just before you would forget.

Learn it so it stays. Read it, test yourself, then recall it on two different days. The app schedules that for you, free.

Start this lesson →