- Inside each application service (embedded routing).
- As a separate program that sits between the app and the shards (a proxy/router).
- Inside the database itself (database-side routing).
- Routing inside application services
Each microservice contains the routing logic so it can contact the correct shard directly. For example, the profile service receives Chris’s request, computes that user ID 41 hashes to shard 3, and connects straight to shard 3. This is the most direct path and gives the lowest latency since there’s no extra hop.

If services are not updated in lockstep after a sharding rule change, routing mismatches can cause serious consistency and availability problems. Coordinate deploys and use feature flags or a central configuration source to reduce risk.
- Routing as a separate proxy
An alternative is a dedicated router that sits between services and shards. All services send requests to this proxy, and the proxy computes the target shard.
- Routing on the database side
You can let the database handle routing. Any shard can receive a request and forward it to the shard that holds the data. This minimizes work for application developers, but you lose some flexibility because routing behavior is governed by the database.

Consider Lena, a pop star with 40 million followers. Suppose Lena’s user ID hashes to shard 2. Each shard has an approximate capacity (for example, 100,000 reads per second). When Lena posts, millions of her followers may try to read that post, concentrating massive read traffic on shard 2. Meanwhile shards 0, 1, and 3 might only be handling ~20,000 reads/sec each. Shard 2 becomes overwhelmed, and everyone whose data lives on shard 2 experiences degraded performance. This concentrated overload on a single shard is called a hotspot. When caused by an extremely popular user, it’s commonly called the celebrity problem. There are three common mitigation techniques; teams often combine them.
- Move the hot user to a dedicated shard (directory-based exception)
One pragmatic approach is to give Lena her own shard. Practically, the routing layer contains a directory mapping Lena’s user ID to a dedicated shard. At routing time the router checks the directory first.

-
Use a read cache
Most hotspot traffic is reads. A cache (CDN, edge cache, or in-memory cache) can absorb the majority of read requests so they never hit the shard. Remember: caches only help reads — writes must still reach the datastore. -
Spread writes using salting (shard key diversification)
Large-scale applications often duplicate writes to satisfy different access patterns. For example, when Chris likes Lena’s post, one copy of that like might be stored with Chris’s data (so Chris’s activity is preserved on his shard) and another copy stored with the post (so the like count and followers-of-the-post are local to the post’s shard). Without these patterns, millions of likes would concentrate write traffic on the shard that stores Lena’s post.
500), create multiple salted keys like 500-1, 500-2, …, 500-10. Each new like is written under one salted key (commonly chosen at random). Because even a small change to the shard key drastically changes the hash result, salted keys are likely to hash to different shards. Note that salted keys map onto existing shards — you’re not adding machines — but likes will be spread across shards rather than all landing on shard 4.
The trade-off is that computing an aggregate (like total like count) now requires reading all 10 salted keys from potentially different shards and summing them. Therefore salting is applied selectively, only to the hottest keys.
The routing layer keeps a list of which keys are salted so it knows whether to write to a single salted key (for an individual like) or to read multiple salted keys (for an aggregate view).
Salting reduces per-shard write pressure at the cost of more complex reads (you must aggregate across salted keys). Use it only for very hot keys and track salted keys in the routing metadata.
Every read first goes to the cache; if the cache has the answer, the response is returned immediately and the routing layer is not involved. If the cache misses, or the request is a write, the request reaches the routing layer. The routing layer then evaluates:
-
Is this request about likes on Lena’s post?
- If yes (a like write), pick one of the salted keys at random and send the write to the shard responsible for that salted key.
- If yes (a like read for total count), read all salted keys and sum the results.
-
Is this any other request for Lena (for example, opening her profile or reading her posts)?
- If yes, route directly to Lena’s dedicated shard (shard 4 in the example).

As the user base grows, normal users like Chris and Leo increase in number until all shards start to fill. The typical response is to add more shards and change the hashing rule (for example, divide by the new number of shards instead of 4). But changing the shard count changes the hash-to-shard mapping for existing data, so many records must move. For example, suppose the hash for Chris is 4,821,371. With 4 shards:
