Kafka in Production · Kafka Patterns and Operations

Keys, Partitioning and Hot Partitions

Choose message keys for ordering and balance, spot hot partitions, and understand why adding partitions breaks key-to-partition mapping.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

An experiment with key choice: keying by tenant puts one huge tenant's traffic on one partition (a hot partition), keying by order ID spreads load but loses per-tenant ordering, and adding partitions to a topic moves keys to different partitions.

The idea

  • The default partitioner hashes the key: the same key always goes to the same partition, as long as the partition count doesn't change.
  • Choose keys by what must stay ordered (an account, an order), with enough distinct values to spread load.
  • Skewed keys create hot partitions: one consumer does most of the work while others idle.
  • Adding partitions remaps keys, breaking ordering for keys during the change; plan partition counts up front (or create a new topic and migrate).

The code

Key choice, skew and repartitioning
import os
from collections import Counter
from confluent_kafka import Producer
from confluent_kafka.admin import AdminClient, NewPartitions, NewTopic

BOOTSTRAP = os.environ["KAFKA_BOOTSTRAP"]
admin = AdminClient({"bootstrap.servers": BOOTSTRAP})
admin.create_topics([NewTopic("events", num_partitions=4, replication_factor=1)])["events"].result()
producer = Producer({"bootstrap.servers": BOOTSTRAP})

def send_all(keys):
    placed = []
    for key in keys:
        producer.produce("events", key=key, value=b"x", on_delivery=lambda err, msg: placed.append((msg.key().decode(), msg.partition())))
    producer.flush()
    return placed

tenants = ["bigcorp"] * 80 + ["shop-a"] * 10 + ["shop-b"] * 10
by_tenant = Counter(p for _, p in send_all(tenants))
print("keyed by tenant, messages per partition:", [by_tenant.get(p, 0) for p in range(4)])

by_order = Counter(p for _, p in send_all([f"order-{i}" for i in range(100)]))
print("keyed by order id, messages per partition:", [by_order.get(p, 0) for p in range(4)])

keys = [f"order-{i}" for i in range(20)]
before = dict(send_all(keys))
admin.create_partitions([NewPartitions("events", 8)])["events"].result()
producer = Producer({"bootstrap.servers": BOOTSTRAP})          # a fresh producer sees the new partition count
after = dict(send_all(keys))
moved = sum(before[k] != after[k] for k in keys)
print(f"after growing 4 -> 8 partitions, {moved} of {len(keys)} keys map to a different partition")
admin.delete_topics(["events"])["events"].result()

Output

keyed by tenant, messages per partition: [10, 80, 10, 0]
keyed by order id, messages per partition: [24, 26, 24, 26]
after growing 4 -> 8 partitions, 10 of 20 keys map to a different partition

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Keying by a low-cardinality or skewed value (country, tenant) and wondering why one consumer is overloaded.
  • No key at all for events that must stay ordered per entity.
  • Increasing partitions on a topic whose consumers rely on key ordering, without a migration plan.

Key points

  • The key decides the partition and the ordering scope.
  • Pick keys with enough cardinality; watch for skew.
  • Adding partitions remaps keys; size topics ahead or migrate deliberately.

Further reading: Producer configs: partitioner · Choosing the number of partitions

Practice questions

What is a hot partition?

Answer

A partition that receives a disproportionate share of messages because of a skewed key, overloading the consumer that owns it.

Why is adding partitions risky for keyed topics?

Answer

The key-to-partition mapping changes, so new messages for a key may land in a different partition than older ones, breaking per-key ordering during the transition.

How do you choose a message key?

Answer

Use the entity whose events must stay ordered (account, order, device), with enough distinct values to spread load evenly.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?