Static vs. Dynamic Hashing: Extendible & Linear Hashing
π‘ Core Intuitionβ
π³ The Everyday Analogy: The Mailroom Wall vs. The Accordion Organizerβ
Imagine managing incoming physical mail for employees in a growing tech company:
- The Static Mailroom (Fixed Cubbies): You nail a wooden shelf with exactly cubby holes (buckets) numbered 0 through 9 to the wall. You file mail using the last digit of the employee ID. When the company grows from 50 to 5,000 employees, cubbies overflow. Letters spill onto the floor, so you tape cardboard boxes underneath (overflow chains). Searching for one letter requires sifting through an endless cardboard chain ( time). To fix this, you must tear down the entire wall, buy 100 new cubbies, and manually re-sort every single letter in the building ( full rehash).
- The Dynamic Mailroom (Extendible Hashing):
Instead of physical wall cubbies, you keep an expandable digital index board (The Directory) and a set of portable storage bins (Buckets).
Each bin handles a binary prefix (e.g.
0and1). When bin0fills up, you do not touch bin1! You simply divide bin0into00and01, leaving the rest of the mailroom completely undisturbed. The directory expands smoothly and incrementally as data grows.
π» Bridging to Computer Scienceβ
In database storage, Hashing maps search keys directly to disk block addresses using a mathematical hash function , achieving theoretical point lookups. However, enterprise database tables are dynamic: rows are constantly inserted and deleted.
- Static Hashing uses a fixed number of address buckets. Over time, it suffers from performance degradation (long overflow chains) or massive storage waste.
- Dynamic Hashing (Extendible Hashing and Linear Hashing) allows the hash address space to grow and shrink smoothly without requiring a complete database reorganization.
π Core Deep-Dive & Architectural Conceptsβ
1. Static Hashing Architecture & Inherent Flawsβ
Definition: In Static Hashing, a fixed collection of physical disk blocks (called Buckets) is allocated on disk numbered from to .
A deterministic hash function maps any search key to a bucket address:
Key K βββΊ [ Hash Function h(K) ] βββΊ Bucket Address (0 to M-1) βββΊ Physical Disk Block
Collision Resolution in Static Storageβ
When two distinct keys produce the same bucket address (), a collision occurs. If the targeted disk block is already full:
- Open Addressing (Linear Probing): The engine searches sequentially for the next available slot in subsequent physical blocks. However, this disrupts locality and severely degrades performance on secondary disk storage.
- Overflow Chaining (Closed Addressing): The full primary bucket allocates an overflow block linked via a pointer, forming a linked list of overflow pages.
Primary Bucket 2: ββββββββββββ¬βββββββββββ¬ββββββββββ
β Key: 102 β Key: 242 β NextPtr βΌβββΊ Overflow Block: ββββββββββββ¬βββββββββββ
ββββββββββββ΄βββββββββββ΄ββββββββββ β Key: 382 β NULL β
ββββββββββββ΄βββββββββββ
The Three Fatal Flaws of Static Hashingβ
- Bucket Overflow Chains: As data accumulates, overflow chains grow long. Searching for an un-indexed key requires traversing multiple chained disk blocks, degrading access down to sequential disk I/Os.
- Catastrophic Reorganization Cost: If the database expands significantly, the only remediation is to allocate a larger bucket array (e.g. buckets) and recompute for every record in the tableβhalting production operations for hours.
- Storage Fragmentation: If initial allocation is oversized to prevent collisions, empty or underutilized blocks waste large amounts of expensive disk space.
2. Extendible Hashing Architectureβ
Definition: Extendible Hashing is a dynamic hashing technique that decouples the hashing directory from physical disk buckets, allowing individual buckets to split independently while the directory grows dynamically via doubling.
Core Structural Componentsβ
- The Directory:
- An array of pointers residing in memory.
- is the Global Depth: it denotes the number of bits of the hash value currently used to index into the directory.
- The directory contains entries numbered from to in binary.
- The Buckets:
- Physical disk blocks storing data records.
- Each individual bucket has a Local Depth ().
- denotes the number of hash bits that all keys residing inside that specific bucket have in common.
The Golden Invariant of Extendible Hashing:
The number of distinct directory entries pointing to a bucket with local depth is:
Global Depth d = 2
Directory: Buckets:
ββββββββ
β 00 βββββββββββββββΊ Bucket A (Local Depth d' = 2): Keys starting with 00
ββββββββ€
β 01 βββββββββββββββΊ Bucket B (Local Depth d' = 2): Keys starting with 01
ββββββββ€
β 10 βββββ
ββββββββ€ βββββββββββΊ Bucket C (Local Depth d' = 1): Keys starting with 1
β 11 βββββ
ββββββββ
(Notice that Bucket C has . Since , exactly directory entries (10 and 11) point to Bucket C).
3. Step-by-Step Extendible Hashing Insertion Protocolβ
To insert a record with search key :
- Calculate the hash value in binary.
- Examine the first bits of to locate the corresponding directory index.
- Follow the directory pointer to the physical bucket.
- If the bucket has free space: Insert the record.
- If the bucket is full (Overflow):
- Check the overflowing bucket's local depth against global depth :
Case A: If (Local Depth is Less Than Global Depth)β
- The directory does NOT double! Directory size remains .
- Allocate a new empty bucket on disk.
- Increment the local depth of both the old bucket and the new bucket:
- Redistribute the records of the overflowing bucket between the old bucket and the new bucket based on the bit.
- Update directory pointers that previously pointed to the old bucket.
Case B: If (Local Depth Equals Global Depth)β
- Directory Doubling: The directory must double in size!
- Increment the global depth:
- The directory doubles from entries to entries. Each previous entry is split into two entries ( and ).
- Allocate a new bucket and increment local depth:
- Rehash and redistribute the records of the overflowing bucket using bits.
- Update the directory pointers. All other un-split buckets now have two directory pointers pointing to them.
4. Step-by-Step Solved Problem: Extendible Hashing Insertionβ
Problem Statementβ
Assume a bucket capacity of 2 records. Initial global depth and local depth . Insert records whose 4-bit binary hash values are:
Stepwise Execution Traceβ
- Initial State ():
- Directory has entries:
[0]and[1]. - Bucket A (): points from
0. - Bucket B (): points from
1.
- Directory has entries:
- Insert and :
- First bit of both is
0. Both insert into Bucket A. - Bucket A is now full:
[ 0001, 0100 ]().
- First bit of both is
- Insert :
- First bit is
0. Bucket A is full! - Here, local depth and global depth ().
- Directory Doubles! Global depth becomes . Directory entries become
00, 01, 10, 11. - Bucket A splits into Bucket (, keys starting with
00) and Bucket (, keys starting with01). - Redistribute keys:
- starts with
00Bucket . - starts with
00Bucket . - starts with
01Bucket .
- starts with
- Directory pointers:
00Bucket ()01Bucket ()10Bucket B ()11Bucket B ()
- First bit is
- Insert and :
- Both start with
1. Directory entries10and11point to Bucket B. - Both insert into Bucket B. Bucket B is now full:
[ 1010, 1100 ]().
- Both start with
- Insert :
- First two bits are
01. - Directory
01points to Bucket , which currently has record (0100). - Inserts directly into Bucket with zero splits! Bucket now holds
[ 0100, 0111 ].
- First two bits are
Conclusion: Only the overflowing bucket split. No global table scan or database-wide rehashing was performed!
5. Linear Hashing Architectureβ
Definition: Linear Hashing is an alternative dynamic hashing algorithm that completely eliminates the in-memory directory.
Instead of doubling a directory, Linear Hashing splits buckets sequentially in linear round-robin order () using a split pointer () and a family of hash functions: Where is the initial number of buckets and is the current round number.
Key Mechanism:
- When any bucket overflows (creating an overflow block), the bucket pointed to by the split pointer is split, and advances:
- When reaches the end of the bucket range (), all buckets have doubled. Pointer resets to , and round number increments .
- Advantage: Zero directory memory overhead. Space allocation grows smoothly linearly rather than exponentially.
6. Architectural Comparison Matrixβ
| Property | Static Hashing | Extendible Hashing | Linear Hashing | B+ Tree |
|---|---|---|---|---|
| Directory Required? | No | Yes (Doubles in RAM) | No (Direct computation) | No (Tree structure) |
| Growth Mode | Static (Manual rehash) | Dynamic (Doubling) | Dynamic (Linear) | Dynamic (Splits) |
| Point Query Cost | (worst ) | Strictly (2 memory accesses) | (3β4 I/Os) | |
| Range Query Support | None | None | None | Excellent ( sequential) |
| Space Utilization | Poor (spills/wastes) | Moderate () | Moderate () | High () |
π Architecture / Visual Blueprintβ
π In The Real World: Production Case Studyβ
PostgreSQL Hash Indexes & In-Memory Redis Dictionariesβ
-
PostgreSQL Hash Indexes (
USING HASH):- Historically, PostgreSQL hash indices were not recommended because they were not Write-Ahead Logged (crash-unsafe).
- In PostgreSQL 10+, hash indexes were completely re-architected with full WAL logging and concurrency control based on a variant of Linear Hashing.
- For pure equality queries on large string keys (e.g. searching 64-character SHA-256 tokens:
WHERE token = 'a8f4c...'), PostgreSQL hash indexes occupy significantly less disk space than B+ Trees while offering consistent single-hop lookups.
-
Redis In-Memory Progressive Rehashing:
- The open-source in-memory store Redis maintains two hash tables (
ht[0]andht[1]). - When the load factor exceeds capacity, Redis triggers Progressive Rehashing: instead of rehashing millions of keys in one blocking pause, Redis rehashes a small batch of keys during every client command, seamlessly migrating buckets in background time slices without latency spikes.
- The open-source in-memory store Redis maintains two hash tables (
π― Exam & Interview Pitfall Checkβ
Question 1: Under what exact condition does the directory double in Extendible Hashing?
Answer: The directory doubles if and only if an overflow occurs in a bucket whose Local Depth equals the Global Depth (). If a bucket overflows when its local depth is strictly less than the global depth (), the bucket splits into two new buckets, its local depth increments by , and existing directory pointers are redirectedβwithout doubling the directory.
Question 2: Why are Hashing techniques virtually never used as primary clustered indices in relational databases?
Answer:
Relational queries heavily rely on range scans, sorting, and inequalities (WHERE age BETWEEN 25 AND 35, ORDER BY timestamp DESC, LIMIT 10).
Hash functions uniformly randomize key distributions across buckets to eliminate collisions, completely destroying lexicographical and numerical ordering. As a result, range queries on a hash index degenerate into a full scan of all buckets, making B+ Trees the universal default for relational storage engines.
Trap 1: Assuming that a directory doubling splits all buckets in the database. When the directory doubles from to , only the single overflowing bucket is split! All other buckets remain untouched on disk; the new directory simply creates two duplicate pointer entries for each of the un-split buckets.
Trap 2: Confusing the number of directory pointers to a bucket. A bucket with local depth in an Extendible Hashing scheme with global depth is pointed to by exactly directory entries. If and , exactly directory entries point to that single physical bucket.