Chapter 07 · Partitioning and Sharding: Range, Hash, Directory, and Consistent Hashing

Range Partitioning vs Hash Partitioning: Locality, Skew, and Query Routing

Compare range and hash partitioning with the same keys to make locality, skew, monotonic hot spots, and routing fan-out observable.

Beginner → Advanced90–110 minutesrange-vs-hash simulationVendor-neutral · Python 3.13.5 simulatorFree/local · no database or cloud requiredLast reviewed: August 2026

Learning outcomes

AtlasMart now has a partitioned order stream, but the first key design question is not “range or hash?” in isolation. The choice changes locality, skew, routing precision, split behavior, and the number of partitions touched by a query. This lesson compares both mechanisms with one key set so the tradeoff is observable rather than memorized.

01

Route identical keys through ordered ranges and a stable hash and compare resulting ownership.

02

Explain why range partitioning preserves ordered locality but can create monotonic hot spots.

03

Explain why hash partitioning tends to spread keys while forcing ordered/range queries to scatter.

04

Use routing metadata to reason about targeted queries, split points, and stale-route failures.

05

Diagnose a monotonic-key hotspot and choose a partition strategy based on access patterns rather than evenness alone.

1. Range partitioning: preserve order, expose boundaries

Range partitioning assigns intervals of an ordered key space to partitions—for example, order IDs 1000–1009 to R0 and 1010–1019 to R1. A range query can often be routed to one or a few adjacent partitions because order is preserved. Splitting is conceptually direct: choose a boundary within an overloaded range and create two ownership intervals.

The danger is monotonic growth. If new order IDs always increase and the final open-ended range owns all future IDs, every new write targets the same partition until the system splits or changes the key design. Range systems can automate splits, but the hotspot exists between detection and successful movement.

2. Hash partitioning: spread values, give up natural order

Hash partitioning transforms the partition key through a deterministic hash before assigning ownership. Good hashes make nearby input values land far apart, which reduces dependence on the input distribution and helps spread monotonically increasing IDs. But a business query such as “orders 1012 through 1018” no longer corresponds to one contiguous ownership interval; it may require fan-out or a separate index/materialized view.

Property Range partitioning Hash partitioning
Point lookup Targeted if boundary map is known Targeted if hash + token/slot map is known
Ordered range scan Usually local to one/few adjacent partitions Often scatter-gather without another access path
Monotonic key writes Can concentrate at the newest range Usually spread by hash
Skew from one hot key Still hot on one owner Still hot on one owner
Split decision Choose key boundary / split range Move hash slots/token ranges or add ownership points

3. Routing metadata is part of correctness

A range router needs ordered boundaries and owners; a hash router needs the hash algorithm plus slot/token ownership. Both need a versioned metadata view. If a client uses an old map after a split, a correct system must reject/redirect or safely forward the request rather than silently accepting ownership under conflicting maps.

text · illustrative range-routing epochs
epoch=12
range=[1000,1020) -> shard-a
range=[1020,1040) -> shard-b

# after split
epoch=13
range=[1000,1010) -> shard-a
range=[1010,1020) -> shard-c
range=[1020,1040) -> shard-b

The map says where a request should go, not whether the chosen key is semantically good. A perfectly consistent routing table can still create a disastrous hotspot.

4. AtlasMart lab — same keys, different locality

Environment: Python 3.13.5 standard library, deterministic SHA-256 hashing, four logical range partitions and four logical hash partitions. No product-specific defaults are implied.

python · range-versus-hash routing simulator
import hashlib
from collections import Counter

keys = list(range(1000, 1040))
RANGES = [
    (1000, 1010, "R0"),
    (1010, 1020, "R1"),
    (1020, 1030, "R2"),
    (1030, 2000, "R3"),
]


def range_route(k):
    for lo, hi, name in RANGES:
        if lo <= k < hi:
            return name
    raise KeyError(k)


def hash_route(k):
    h = int.from_bytes(hashlib.sha256(str(k).encode()).digest()[:8], "big")
    return f"H{h % 4}"

range_counts = Counter(range_route(k) for k in keys)
hash_counts = Counter(hash_route(k) for k in keys)
print("INITIAL KEYS")
print("range counts:", dict(sorted(range_counts.items())))
print("hash counts:", dict(sorted(hash_counts.items())))

scan = list(range(1012, 1019))
print("\nRANGE SCAN 1012..1018")
print("range partitions touched:", sorted({range_route(k) for k in scan}))
print("hash partitions touched:", sorted({hash_route(k) for k in scan}))

new_monotonic = list(range(1040, 1080))
print("\nMONOTONIC INSERT BURST")
print("range target counts:", dict(Counter(range_route(k) for k in new_monotonic)))
print("hash target counts:", dict(sorted(Counter(hash_route(k) for k in new_monotonic).items())))
print("wrong assumption: 'ordered keys are automatically balanced' -> all new range writes hit R3")
print("tradeoff: hashing improves distribution but destroys simple ordered locality")
text · verified output from the simulator
INITIAL KEYS
range counts: {'R0': 10, 'R1': 10, 'R2': 10, 'R3': 10}
hash counts: {'H0': 12, 'H1': 10, 'H2': 5, 'H3': 13}

RANGE SCAN 1012..1018
range partitions touched: ['R1']
hash partitions touched: ['H0', 'H1', 'H2', 'H3']

MONOTONIC INSERT BURST
range target counts: {'R3': 40}
hash target counts: {'H0': 9, 'H1': 16, 'H2': 8, 'H3': 7}
wrong assumption: 'ordered keys are automatically balanced' -> all new range writes hit R3
tradeoff: hashing improves distribution but destroys simple ordered locality

Diagnosis

The ordered scan touches one range partition but several hash partitions. The monotonic burst routes every new key to R3 under the intentionally static range map, while the hash scheme spreads the burst. That is not evidence that hashing is universally better; it is evidence that the access pattern and update distribution differ.

Wrong fix

Choosing a hashed key solely because it “looks balanced” can turn formerly local business queries into scatter-gather requests. The safer process is to list the point lookups, ordered scans, aggregations, write concentration, cardinality, and growth pattern first; then choose or compose keys that optimize the dominant paths.

Check your understanding

  1. Why do range partitions help ordered scans?
  2. Why can increasing IDs create a hot range?
  3. What does hashing sacrifice?
  4. Does hashing eliminate hot keys?
  5. What must be versioned during range splits?
Review the answers

1. Because ordered key intervals map to adjacent ownership ranges, so a bounded scan can often target one or a small number of partitions.

2. All new values fall into the highest/open-ended interval until that range is split or the key strategy changes.

3. Natural adjacency/order in the original key space, so ordered range queries often need fan-out or a secondary access path.

4. No. One identical hot key hashes to one routing unit unless the application deliberately shards/decomposes that key.

5. The routing/boundary metadata so stale clients can be detected, redirected, or forwarded safely during ownership changes.

5. Production judgment

Use ranges when locality and ordered access dominate and you can manage split/hot-range behavior. Use hashing when point access and even distribution dominate and range locality is less important or provided by another view. Composite strategies are common—for example, tenant hash plus time bucket—but every added component changes routing and query fan-out.

Monitor per-range write rate, key-cardinality growth, split backlog, scatter-gather count, per-shard tail latency, and routing redirects. Do not publish raw tenant or customer identifiers in diagnostic labels. The next lesson replaces fixed modulo hashing with consistent hashing so adding/removing nodes does not remap most keys.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.