Design a Distributed Counter
How to design distributed counters for interviews: Redis INCR, sharded counters, CRDTs, approximate counts, write buffering, and read-your-writes trade-offs.
Like counts, view counts, inventory soft caps, and rate limiter buckets all need counters under contention. A single DB row becomes a hotspot instantly. The interview is about spreading writes and stating how stale or approximate a read may be.
Scope with the framework: incr/decr by delta, get current value, optional reset, accuracy vs QPS. Tie to Redis and CAP.
Functional requirements
- Increment(key, delta) and Decrement(key, delta).
- Get(key) → current count (exact or approximate - say which).
- Optional: GetAtLeast / GetApprox for cheaper paths.
- TTL or daily buckets for time-windowed counts.
Non-functional requirements
- Very high write QPS on hot keys (viral posts).
- Low latency reads for rendering counts on pages.
- Durability: losing a few increments on crash may be OK for likes - not for money.
- Horizontal scale without a single-row bottleneck.
Exact and hot do not mix cheaply
If every increment must be globally serialised and durable, throughput dies. Most social counts accept eventual consistency or ±1% error. Billing and stock need different tools (ledger / inventory service).
Approach ladder
| Approach | Pros | Cons |
|---|---|---|
| Single Redis INCR | Simple, atomic, fast | One key hotspot; memory lost on crash unless AOF |
| DB row + retries | Durable | Locks / hot page |
| Sharded counters | Spreads writes | Reads must sum shards |
| Write buffer + flush | Smooths spikes | Delayed visibility |
| CRDT G-Counter / PN-Counter | Multi-region merge | More complex; grow with replicas |
Sharded counter design
- Pick N shards: key#0 .. key#(N-1).
- Increment: choose shard by hash(request_id) or random → INCR that shard.
- Get: MGET all shards and sum (or cache the sum for a second).
- Grow N when write QPS climbs; mention re-sharding pain.
N ≈ 10-100 is a common interview starting point. Reads become O(N). Mitigate with a periodically refreshed total in another key, knowing it lags. For leaderboards you often want sorted sets instead of raw counters.
Async aggregation
Apps enqueue +1 events to Kafka; consumers batch-update shards or a warehouse. UI reads a slightly stale cache. This matches ad click style pipelines when volume explodes.
Multi-region
Active-active: each region owns local shards; a CRDT merge (max per replica id for G-Counters) yields a global value. Or designate a primary region for exact counts and accept cross-region latency. Be explicit which model you pick.
Worked example
- Viral video like counter; target 200k writes/s.
- Use 64 Redis shards video123#i; clients INCR a random shard.
- Read path sums shards every request for admin; public page reads a 1s cached total.
- Nightly job persists totals to durable DB for analytics.
Interview summary
Start with INCR, then shard for hot keys, then async or CRDT for multi-region. State accuracy SLOs. That progression is the distributed counter interview.