Hash Partitioning: Divide and Conquer
Concept. Hash partitioning splits a too-big-for-RAM table into N partitions by hashing a key. Each partition fits in RAM and supports independent processing.
Intuition. When your Listens table is 64 GB but your machine has 6.4 GB of RAM, hash user_id % 16 to split Listens into 16 partitions of 4 GB each. Each listen from Mickey maps to the same partition regardless of input order.
The Hash Partitioning Algorithm
Core Idea
Break the data into B smaller partitions, each chunk holding rows for a subset of keys.
Figure: Hash partitioning through a fixed RAM bufferHash partitioning shown as DISK input on the left, a small fixed RAM buffer in the middle, and DISK partitions on the right. The disk columns are visibly large; RAM is intentionally small to convey that the input file does not fit in RAM. The source file is read one page at a time into a flat one-page input buffer on the left of RAM. A hash function routes each row to output buffers B0, B1, and B2 on the right. Buffers show all six complete rows after processing input pages P1 through P3, before any flush. Each output buffer points horizontally to its final disk partition, shown after the full scan. When a buffer fills, it flushes to its partition file on disk. Color key: red is RAM (volatile and limited), green is disk (persistent), grey is out of focus.
Figure 1. Hash partitioning makes one pass over a too-big-for-RAM file using a small, fixed RAM buffer: one input slot plus one output buffer per partition. A page streams into the input slot, each row hashes by h(user_id) % B and routes to one of ~B output buffers. The instant any buffer fills, it flushes to its partition file on disk and is cleared, so RAM never holds more than that fixed set of buffers no matter how large the input. Partitions are stored on disk, not in memory: the full partitions live on disk, while only the small fixed RAM buffer is resident at any point in time. Rows with the same key always land in the same partition, so later stages can sort, join, or group one partition at a time. See the IO Cost Summary for the cost breakdown.
Algorithm · Hash Partitioning
HashPartition(file, B, key): // B output buffers + 1 input buffer Phase 1: Stream rows into per-bucket buffers for row in file: p = hash(row.key) % B output_buffers[p].add(row) if output_buffers[p].full: write(output_buffers[p] to dbfile_p) // flush a full buffer output_buffers[p].clear() Phase 2: Flush whatever is left flush_all(output_buffers)
Cost
One read of every page and one write of every page: C_r×N + C_w×N, which is 2N IOs for C_r = C_w = 1. The key point: the cost is linear in N.