Core Data Structures
How do we choose a data structure when the real cost is set by the hardware, not by Big-O?
Who this mechanism is for: a developer who writes code, has seen arrays, hash maps and trees in a algorithms course, and knows basic SQL, but has never asked what a data structure costs on a real disk, SSD, CPU cache or multi-core server. You do not need any database knowledge. Every technical term is explained the first time it appears, and again in the glossary at the end. You do not need to remember any other mechanism: whatever idea we need, we explain it here.
The story we will follow
Arjun is a backend engineer at Parcelo, a small company that tracks parcels for online shops. Every time a parcel is scanned (at a warehouse, on a truck, at a depot, at a doorstep) Parcelo stores a scan event: (tracking_id, timestamp, depot, status, weight). Customers ask "where is my parcel?". Shops run reports such as "how many parcels reached Pune last week?". The system is growing from 50 million events to 5 billion.
Lena is the tech lead. She asks one kind of question again and again: "What does this cost on the real machine?" She never accepts "it is O(log n)" as an answer. Scan events arrive from many ingest threads, are held in memory first, and are then written to files on SSD and to cloud object storage. Queries come from a web API and from nightly report jobs.
Parcelo has one rule written on the wall:
Over this mechanism, Arjun meets seven problems, one at the start of each article. Each problem looks like a bug: a table that is too big, a job that suddenly takes ten times longer, a server that gets slower when we add threads. Each one is actually a consequence of what a data structure promises and what it costs. At the end of each article we go back to Arjun and solve the problem with the article's mechanisms.
This is the main idea of this mechanism:
How this mechanism is organized
- 1
A data structure is a bet
Why is a data structure a bet, and why does Big-O no longer tell us which one wins?
CaseThree O(log n) structures, five times apart
- 2
Lookup structures
How do we find the place where key *k* lives (hash tables, B-trees, LSM-trees, tries, skip lists, sparse indexes, perfect hashing, interval trees)?
CaseAn index 45% larger than its data
- 3
Ordering structures
How do we find "what comes next" (heaps, balanced trees, sorting and merging, sliding windows)?
CaseA top-10 query that writes 800 GB
- 4
Probabilistic structures
How do we get answers in tiny memory by accepting a small, controlled error (Bloom filters, HyperLogLog, Count-Min and others)?
Case40 GB to count about a billion
- 5
Bit-level structures
How do we store data near its information-theoretic minimum and still query it (bitmaps, rank/select, succinct structures)?
CaseA 1.25 TB bitmap, and zone maps that skip nothing
- 6
Concurrency-friendly structures
What changes when many threads work at the same time (ring buffers, concurrent maps, immutable structures, safe memory reclamation)?
CaseMore threads, fewer events per second
- 7
Compositions
Which few fixed arrangements explain how real systems combine these structures?
CaseDefend 5 billion events on one page
After that: a summary, hands-on labs, interview questions and a glossary.
One request: do not skip the arithmetic. This mechanism derives many small formulas (page size divided by row size, memory divided by transfer size). The derivations are the content. The structures themselves are only the examples.
What do I do, and what does each option cost?
Choose a lookup structure
You need to find records by key: a static structure, a hash table, a skip list, a B-tree or an LSM-tree? Four questions, each removing more of the design space, then a check of the failure mode.
ChooseChoose an ordering structure
A query needs data in order: a heap, an existing order, an in-memory sort, an external sort or a distributed sort? Four questions, starting with how much order you really need.
ChooseChoose a sketch
An exact answer is too big to keep: which sketch answers your question (membership, distinct count, frequency, quantile, similarity or a sample), and will it survive merging, duplicates and the questions asked later?
ChooseChoose a bit-level structure
A structure is read-mostly and could be packed far smaller: is the saving worth it, and should it be a bitmap, an Elias-Fano list or a packed array with rank/select? Plus when min/max statistics can prune at all.
Choose
Can I see it myself?
Hash table load factor: measure the hyperbola
Fill a linear-probing table to α = 0.5 … 0.95, count probes against the formulas, then time it against chaining.
MeasurePythonB-tree key order: UUIDv4 versus UUIDv7
Insert 5 million rows under a random UUID key and under a time-ordered key into PostgreSQL; compare index size, page fill, insert time and WAL.
Real systemDocker, PythonLSM versus B-tree write amplification, derived and measured
Write small rows and 8 KB rows into RocksDB and LMDB, measure device bytes per user byte, then turn on RocksDB blob files.
Real systemDockerMonkey-style filter allocation
Split one Bloom-filter memory budget across LSM levels equally or Monkey-style, count wasted I/Os per million missing-key lookups.
SimulatePythonSparse index granule size
Index a sorted file of 100 million records every G records and time point lookups for G from 64 to 65,536.
MeasurePythonSliding maximum: naive, deque and panes
Compute a sliding maximum over a 10,000-value window naively, with a monotonic deque, and with panes; then feed the deque its best and worst shapes.
MeasurePython with NumPyTop-k with a bounded heap versus a full sort
Find the top 10 of 20 million numbers with sort | head and with a bounded heap; compare time and peak memory, then read DuckDB's plan.
MeasurePython, sortBloom filter: measure the false-positive curve and the cache cost
Measure a Bloom filter's false-positive rate at 5 to 20 bits per key against the formula, then time lookups in a 10 MB, a 1 GB and a blocked 1 GB filter.
MeasurePython with NumPyCount-Min Sketch: head versus tail
Count a Zipf stream of 10 million events with a Count-Min sketch, compare head and tail errors, then find heavy hitters with Misra-Gries and Space-Saving.
SimulatePython with NumPyHyperLogLog: error, merge and the intersection trap
Check HyperLogLog's error over 20 runs, merge two sketches, then estimate a 1% intersection with HLL and with a Theta sketch.
SimulatePythonDo not average percentiles
Compute the p99 of 100 shards by averaging shard p99s and by merging KLL, t-digest and REQ sketches, against the exact pooled p99.
SimulatePythonRoaring versus plain bitmap versus sorted array
Size sets of row IDs at five densities as a plain bitmap, a sorted array and a Roaring bitmap, then time a 1% intersection in each.
MeasurePythonElias-Fano and the posting-list size
Store a million sorted doc IDs as int32, delta + varint and Elias-Fano; compare size and random-access time.
MeasurePython with NumPyFalse sharing: measure the 64-byte penalty
Eight threads increment their own counters: packed in one line, padded to 64 and 128 bytes, and counted locally.
MeasureC compiler, PythonSPSC ring buffer: head and tail on one line, then on two
Move 100 million items through a single-producer single-consumer ring with head and tail on one line, on separate lines, and with batched publishing.
MeasureC compiler, PythonPersistent tree: measure the cost of path copying
Insert a million keys into a mutable and a persistent binary tree; compare time and memory, hold a snapshot, count shared nodes.
MeasurePythonReader-writer lock against plain mutex on a read-heavy workload
99% reads and 1% writes from 1 to 16 threads under a mutex, a reader-writer lock and an atomic snapshot pointer.
MeasureC compiler, PythonBuild a pruning cascade and count rows touched
Prune 1,000 point-in-range queries over 10 million rows through a zone map, a Bloom filter and a sparse index; then shuffle the rows.
SimulatePython with NumPy
Can I reason about it under questioning?
Interview questions
Short questions on data structures as engines use them, such as hash tables, B-trees, LSM-trees, sorting, sketches, bitmaps and concurrent structures, each with an answer you could give aloud and a link to the full explanation.
Scenarios
Longer problems on choosing and combining data structures, of the kind asked in senior interviews: each one estimated with numbers, diagnosed, and answered with its trade-offs.
The tensions this mechanism takes a position on. Each recurs across mechanisms.
Exactness vs. approximation
For example: sketches, sampling, ANN.
Read-optimized vs. write-optimized
For example: B-tree vs. LSM, merge-on-read vs. copy-on-write.
Generality vs. specialization
For example: general engines vs. purpose-built structures.
Mutability vs. immutability
In-place update vs. append-only + GC; buys reproducibility and cheap concurrency, pays in space and janitorial work.
Space vs. time
For example: compression, indexing, materialization, caching.
Show all 9 trade-offsShow fewer
Precompute vs. compute-on-read
For example: materialized views, ELT vs. ETL, schema-on-read.
Coupling vs. coordination cost
For example: 2PC vs. sagas, schema contracts vs. schema-on-read.
Ordering vs. parallelism
Global order costs serialization; parallelism costs order.
Early binding vs. late binding
When structure is committed; determines where the failure surfaces, at write time or read time.
Ideas this mechanism meets that keep coming back, mechanism after mechanism.
Fixed cost per operation
Fixed cost per operation implies a natural batch size.
The algebra decides
What can be skipped, pushed down, parallelized, merged or precomputed is determined by the algebra of the function, not the layout of the data.
Costs are conserved
Costs are conserved, never removed.
Which way the error points
Variance costs more than volume
Variance costs more than volume.
Show all 14 principlesShow fewer
Independence is assumed
Independence is assumed everywhere and is true almost nowhere.
Immutable storage grows indirection
An immutable substrate grows an indirection layer plus a collector.
Granularity mismatch
Granularity mismatch creates amplification.
Queues explode near full load
Response time explodes as utilization nears 100%.
Randomization replaces coordination
Randomization can replace coordination.
Coordination cannot be made cheaper
Coordination is the only thing you cannot make cheaper.
Retention is paid by the live path
Bound the answer, bound the state
A cap replaces an estimate
Where a value cannot be predicted, systems substitute a cap.