M2

Data Centers: The Backbone of Modern Databases

Latency in a modern data center spans eight orders of magnitude, from a nanosecond on-chip to a hundred milliseconds across the planet.

One data center: seven orders of magnitude of latency Seven latency tiers on one axis, ordered fastest to slowest left to right: L1 cache 1 ns on-chip, RAM 100 ns on-board, SSD 100 microseconds same rack, network within the data center 500 microseconds an aisle away, spinning disk 10 milliseconds same rack, cross-region 50 milliseconds a continent away, cross-continent 100 milliseconds the other side of Earth. The span from end to end is 100 million times. An algorithm that reads from the wrong tier pays up to 10,000,000 times more. Color key: orange is a latency tier, grey is the fast-to-slow axis. One data center: seven orders of magnitude The same byte is 1 nanosecond or 100 milliseconds away, depending on its tier. faster slower L1 cache on-chip 1 ns RAM on-board 100 ns SSD same rack 10 µs Network aisle away 500 µs Disk same rack 10 ms Cross-region continent away 50 ms Cross-continent across Earth 100 ms End to end is 100,000,000×. Read from the wrong tier and one access costs up to 10,000,000× more.
Seven tiers in one data center, fastest to slowest, from L1 cache at 1 ns to a cross-continent round trip at 100 ms. End to end is 100,000,000x.
Play in classInside Microsoft's data centers (video) ↗Notes ↗
M2

What Is a System?

A system is components chosen to work with each other.

Pick one from each bin Three bins of parts, then three cars built from them. Engines: E1 a motorbike good for 130 mph at five thousand dollars, E2 a turbo four good for 150 at twelve thousand, E3 a jet good for 400 at two million. Chassis: C1 Lego good for 2 mph at fifty dollars, C2 steel good for 150 at ten thousand, C3 carbon good for 250 at five million. Wheels: W1 road tyres good for 150 at six hundred, W2 sport at 200 for two thousand, W3 slicks at 250 for fifty thousand. The builds: E3 plus C1 plus W3 is mismatched and does 2 mph for two million and fifty thousand dollars; E3 plus C3 plus W3 is oversized and does 250 mph for seven million; E2 plus C2 plus W1 is right-sized and does 150 mph for twenty-two thousand six hundred. The goal: a car that will do 150 mph on a public road. THE PARTS YOU CAN PICK Engine E1 Motorbike 130 mph $5,000 E2 Turbo four 150 mph $12,000 E3 Jet 400 mph $2M Chassis C1 Lego 2 mph $50 C2 Steel 150 mph $10,000 C3 Carbon 250 mph $5M Wheels W1 Road 150 mph $600 W2 Sport 200 mph $2,000 W3 Slick 250 mph $50,000 WHAT YOU CAN BUILD FROM THEM Mismatched E3 + C1 + W3 2 mph $2.05M · C1 is the bottleneck Oversized E3 + C3 + W3 250 mph $7.05M · 100 mph past the goal Right-sized E2 + C2 + W1 150 mph $22,600 · hits the goal exactly
The goal is a car that will do 150 mph on a road. Pick one row from each bin; the car then runs at its slowest part and costs every part in it. E3+C1+W3 spends $2.05M and travels at walking pace: C1 is the bottleneck. E3+C3+W3 has no bottleneck, but it is oversized: it runs 100 mph past the goal, at $7.05M. E2+C2+W1 hits 150 on all three parts at once for $22,600.
Notes ↗
M2

IO & Storage

The CPU works through cache and RAM, never the SSD, and data moves one page at a time Three tiers left to right. The CPU on the left works only out of its registers and L1, L2, L3 cache, which the hardware fills from RAM in 64-byte cache lines, in nanoseconds. RAM in the middle holds 16 GB divided by 64 MB, which is 256 fixed-size page slots, drawn as a small grid. The SSD on the right holds 128 GB divided by 64 MB, which is 2048 pages, drawn as a larger grid of the same-size cells; one is highlighted orange as it is paged into a free RAM slot, labelled one page, 64 MB, one IO, transfer-bound at about 5 GB per second so about 13 milliseconds. When RAM fills, a page is evicted and the next is paged in, so all 2048 SSD pages stream through the 256 RAM slots one page at a time. Color key: orange is a page in motion (the unit of one IO), grey is structure. The CPU never touches the SSD Pages move SSD → RAM, one page = one IO. The CPU works out of cache, filled from RAM. CPU registers · sub-ns L1 / L2 / L3 cache ~1 to 10 ns works here, never the SSD RAM · 16 GB 16 GB ÷ 64 MB = 256 pages 6 of 256 slots shown SSD · 128 GB 128 GB ÷ 64 MB = 2048 pages 24 of 2048 pages shown load 1 page = 1 IO Mickey's listens · 64 MB SSD ~5 GB/s = ~13 ms cache lines · 64 B ~100 ns to RAM When RAM fills, evict a page and load the next: 2048 SSD pages stream through 256 RAM slots, one at a time.
The CPU computes only out of its registers and cache; it does not operate on the SSD directly. The SSD gives RAM a 64 MB page per IO, and RAM feeds the CPU 64-byte cache lines in nanoseconds. That page costs the same whether the query wants one row or the whole block, and at ~5 GB/s a 64 MB transfer takes ~13 ms, so cost is page count. **Paging** refers to the overall steps in moving the needed subset of pages in-n-out of RAM.
Notes ↗
Why paging matters: the speed gap scaled to human time Four storage tiers left to right, each access time rescaled as if one L1 cache hit took 1 second. L1 cache is 1 second, RAM is 1.5 minutes (100 times slower), SSD is 3 hours (another 100 times), and a hard disk is 4 months (another 1000 times). The point: a disk read feels like a season when a RAM read feels like a short pause, which is why paging keeps the working set in the fast tiers. Color key: orange is a storage tier and its human-scaled wait, grey is the slowdown factor between tiers. Why paging matters: the speed gap, in human time If one L1 cache hit took 1 second, each tier would feel this long. L1 cache ~1 ns 1 second a blink ×100 RAM ~100 ns 1.5 minutes a short pause ×100 SSD ~10 µs 3 hours an afternoon ×1000 Hard disk ~10 ms 4 months a whole season
Rescale latency so an L1 cache hit takes one second. RAM becomes 1.5 minutes, an SSD read becomes 3 hours, and a hard disk read becomes 4 months. Paging keeps the working set in RAM. A query that reads from disk on every page pays the hard disk or SSD cost once per page.
Optional, recommendedHow does computer memory, cache, and storage work? (video) ↗Notes ↗
IO Cost Model: Algorithmic Complexity for Big Data

Small Data vs Big Data: Where the Cost Sits

  • Small data (< 10 GB): count CPU operations. Everything fits in RAM, so we measure CPU costs.

  • Big data (> 100 GB): count IOs, pages read and written. IO costs dominate; CPU costs are trivial by comparison.

Notes ↗
IO cost notation The IO cost formula and the symbols it uses. IO cost equals C_r times the pages read plus C_w times the pages written. C_r is the time to read one page and C_w the time to write one page, both shown in orange. P(R) is the number of pages in table R and T(R) = n is the number of rows (tuples) in R. Color key: orange marks the two per-page costs C_r and C_w. IO COST Cr × pages read + Cw × pages written Cr time to read one page Cw time to write one page P(R) number of pages in table R T(R) = n number of rows in R
Cost counts page I/Os, not CPU work. Cr and Cw are the time to read or write one page; P(R) is the pages in table R and T(R) = n its row count. Every algorithm's cost is Cr × pages read + Cw × pages written.
Notes ↗
Three algorithms: count the pages Three algorithms and their IO cost, revealed one at a time. Example 1, basic: read and write every page once, cost C_r times P(R) plus C_w times P(R). Example 2, multi-pass: read every page seven times and write three times, cost C_r times seven P(R) plus C_w times three P(R). Example 3, logarithmic: read log P(R) passes and write a tenth of the pages per row, cost C_r times log P(R) times P(R) plus C_w times 0.1 P(R) times T(R). Color key: orange is the cost formula. Example 1 · Basic read / write Read and write every page once. Cr · P(R) + Cw · P(R) Example 2 · Multi-pass Read every page 7 times, write 3 times. Cr · 7 · P(R) + Cw · 3 · P(R) Example 3 · Logarithmic Read log P(R) passes; write 0.1 · P(R) pages per row. Cr · log P(R) · P(R) + Cw · 0.1 · P(R) · T(R)
Example 1 reads and writes every page once; Example 2 makes seven read passes and three write passes; Example 3 reads log P(R) passes and writes a tenth of the pages per row. The cost is the page count; the CPU work is negligible.
Notes ↗
IO Cost Model: Algorithmic Complexity for Big Data

Calculating Read and Write Costs

The cost of reading one page, and of an algorithm:

$$ C_r = \text{access time} + \frac{\text{page size}}{\text{scan speed}} $$

$$ \text{IO cost} = C_r \times \text{pages read} + C_w \times \text{pages written} $$

Device (2025+) Access time Scan speed Cr, one 64 MB page
RAM 100 ns 100 GB/s 0.00064 s
SSD 10 μs 5 GB/s 0.01281 s
HDD 10 ms 100 MB/s 0.65 s
Network 1 μs 10 GB/s 0.00640 s

Page size is 64 MB unless a problem says otherwise; units are decimal (1 MB = 1,000,000 bytes, 1 GB = 1,000,000,000 bytes). In practice a write can cost more than a read; unless a problem says otherwise, CS145 uses Cw = Cr.

Notes ↗
IO Cost Model: Algorithmic Complexity for Big Data

The official CS145 IO equations

The official CS145 IO equations
Notes ↗
M2

Problems: IO Cost Model

Problems: IO Cost Model

Problem 1: Table Size Calculation

The Spotify Songs table has 500 million rows with an average row size of 1024 bytes. Calculate:

  1. Total size in MB

  2. Number of 64 MB pages needed

Show Solution

Total size

500,000,000 rows × 1024 bytes = 512,000 MB = 512 GB

Number of pages

512,000 MB ÷ 64 MB = 8,000 pages


Reference: iocosts-block1
Notes ↗
Problems: IO Cost Model

Problem 2: Read Cost Comparison

Calculate the time (in seconds) to read 100 pages from different storage devices.

Show Solution

RAM

100 × (0.0000001 s + 0.00064 s) = 0.064 s

SSD

100 × (0.00001 s + 0.0128 s) = 1.281 s

HDD

100 × (0.01 s + 0.64 s) = 65 s (about 1,000× slower than RAM)

Reference: iocosts-block1
Notes ↗
Problems: IO Cost Model

Problem 3: Cache Hit Rate Impact

Read 200 pages through a cache. RAM holds 90% of them. Of the pages RAM misses, 75% are on SSD and 25% on HDD. What is the total read time?

Show Solution

How a cached read works, for 200 page reads. Every read checks RAM first, 200 times 0.00064 s, 0.128 s. 180 reads (90%) hit and are served from RAM; 20 (10%) miss. 15 of the misses come from SSD, 0.192 s, and 5 from HDD, 3.25 s, shown in orange. Total 3.57 s: the 5 HDD reads are 2.5% of the requests and 91% of the time.

Figure 1. Every read pays the RAM check (0.128 s); 180 stop there. The 20 misses split into 15 SSD fetches (0.192 s) and 5 HDD fetches (3.25 s), for 3.57 s in total. The 5 HDD reads are 2.5% of the requests and 91% of the time (3.25 / 3.57).

Total time: 3.57 s. The RAM check is paid on every read, and the few reads that fall to HDD dominate.

Notes ↗
Problems: IO Cost Model

Problem 4: Mixed Read/Write Operations

You need to:

  • Read 30 pages

  • Write 10 pages

Calculate total cost for each storage device.

Show Solution

Cost formula

Total = 30 × CR + 10 × CW

Reads and writes cost the same on these devices, so this is 40 × CR.

RAM

40 × 0.00064 s = 0.026 s

SSD

40 × 0.01281 s = 0.512 s

HDD

40 × 0.65 s = 26 s

Notes ↗
Problems: IO Cost Model

Problem 5: Network vs Local Storage

Compare reading 1 page from:

  1. Local RAM

  2. Local SSD

  3. Local HDD

  4. Network RAM (RAM on another machine)

The data movement for a Network RAM read. The 64 MB page starts in RAM on a remote machine. You ask for it, the remote machine reads it from its RAM (0.00064 s), it travels back over a 10 GB/s network (0.0064 s, the dominant cost), and you write it into your own RAM (0.00064 s), for 0.0077 s total, which still beats a local SSD read at 0.0128 s.

Figure 2. A Network RAM read. The page sits in RAM on a remote machine; you request it, read it from remote RAM, send it back, and write it into your RAM. The network hop (0.0064 s) dwarfs the two RAM touches (0.00064 s each). Total 0.0077 s, still under a local SSD read (0.0128 s).

Show Solution

Network RAM

Remote read 0.00064 s + network hop 0.0064 s + local write 0.00064 s = 0.0077 s. The network hop (64 MB ÷ 10 GB/s) dominates.

Versus local storage

The single-page costs on the local devices are the same ones from Problem 2: RAM 0.00064 s, SSD 0.0128 s, HDD 0.65 s.

Ranked: Local RAM < Network RAM (0.0077 s) < Local SSD (0.0128 s) < Local HDD (0.65 s).

Notes ↗
Problems: IO Cost Model

Key Takeaways

  1. HDDs and SSDs are much slower than RAM for random access

  2. Cache hit rates matter enormously - even 5% HDD access can dominate

  3. Network RAM ≈ Local SSD in performance

  4. Page size affects random access penalty - larger pages amortize seek cost

  5. Moving forward: We'll count IOs (1 page = 1 IO) to analyze algorithm complexity

Notes ↗
M2

Hashing: From Data to Algorithms

A hash function maps a key to a fixed-size bucket id, so you can find or insert in expected O(1) by reading the page where the bucket lives.

Integer hashing with h(x) = x % 10 Inputs 42, 157, 1042, 89 are hashed by x mod 10 into ten buckets. 42 and 1042 both land in bucket 2, a collision (shown in orange); 157 lands in bucket 7 and 89 in bucket 9. Color key: orange is the colliding bucket in focus, grey is the other buckets. Integer hashing: h(x) = x % 10 Same modulo, different inputs, same bucket = a collision Input → h(x) Buckets [0–9] 0 1 2 3 4 5 6 7 8 9 89 % 10 = 9 89 42 % 10 = 2 42 157 % 10 = 7 157 1042 % 10 = 2 2 1042
h(x) = x % 10 maps four integers into ten buckets numbered 0 through 9. 42 and 1042 both land in bucket 2 (orange) because both end in 2, while 157 and 89 fall in distinct grey buckets. The collision is the point: an index must handle two or more keys landing in the same bucket. Chaining stores a list per bucket.
Notes ↗
M2

Case Study 2.1: How Uber and Google Maps index locations

Uber's H3 and Google's S2 map 2-D coordinates to 1-D cell IDs.

Why hexagons, not squares: a hexagon's neighbours are all the same distance Two grids compared. On the left a square cell has eight neighbours at two different distances: its four edge neighbours are one cell away, its four corner neighbours are farther by a factor of root two, so the ring a radius query must read is uneven. On the right a hexagon has six neighbours, every one exactly the same distance from the centre, so the ring is even in all directions. That uniform neighbour distance is why map systems grid the world into hexagons. Color key: orange is the query cell and, for the hexagon, its single even neighbour ring; grey is the grid; dashed circles are neighbour distances. Why hexagons, not squares? A radius query reads a cell plus its neighbour ring. The ring is only even if the neighbours are. edge: d corner: 1.4d Squares 8 neighbours, TWO distances d Hexagons 6 neighbours, ONE distance
Cell shape determines the neighbor ring. A square has eight neighbors at two distances: four edge-neighbors one cell away, four corner-neighbors farther by a factor of root two. A hexagon has six neighbors, each at the same distance from the center, which yields a symmetric ring for a radius query. Hexagons approximate a circle better than squares.
Notes ↗
Nearby points share a cell, far points do not The three Stanford campus buildings versus SF Mission, computed with H3. At resolution 8 (zoomed out) the three campus buildings get the identical code 8828347417fffff, one shared cell, while SF Mission, 29 miles away, gets a different code. At resolution 12 (zoomed in) the three campus codes split into three cells, differing in the fine digits at the end (orange), and SF stays different. Color key: orange is where the campus codes differ at resolution 12, black is shared. Nearby points share a cell, far points do not Three Stanford buildings vs SF Mission, computed with H3. Campus shares a cell at res 8 and splits at res 12. Resolution 8 · zoomed out NVidia8828347417fffff Packard8828347417fffff Gates8828347417fffff campus: one cell, one code SF Mission88283082a1fffff 29 mi away: a different cell Resolution 12 · zoomed in NVidia8c28347416257ff Packard8c2834741602bff Gates8c28347416021ff campus: three cells now SF Mission8c283082a06bbff still a different cell
H3 codes for three Stanford campus buildings and SF Mission. At resolution 8 the three campus buildings map to 8828347417fffff and SF Mission, 29 miles away, maps to a different cell. At resolution 12 the three campus buildings split into three cells, differing in the trailing digits (orange), and SF Mission remains in a different cell.
Notes ↗
Geo-hashing: a location becomes a cell code Order pins start as raw latitude and longitude. Lay H3's hexagons over the map and every pin falls in one hexagon, which has a cell code. Pins in the same hexagon, three Stanford buildings here, share one code; a far pin in San Francisco gets a different code. The hash is just which hexagon a point lands in. Color key: orange is a shared cell and the pins in it, grey is the hex grid, dark dots are orders. Step 1, the hash: a location becomes a cell code Every order takes the code of the hexagon it falls in. Nearby orders land in the same hexagon. 3 Stanford orders SF order, far away 8828347417fffff 88283082a1fffff
H3 maps each food-delivery order's latitude and longitude to a cell ID. The system stores 10 million orders by that ID, so nearby orders share one and distant orders fall under different ones.
Notes ↗
Geo-hashing lookup: a radius query is a bounded handful of cell reads At a resolution where one neighboring ring covers two kilometers, the query reads the driver's cell and its six neighbors. Those seven cell identifiers may occupy seven pages. The system then checks exact distances for the candidate food delivery orders. Other radius and resolution combinations may require more rings. Step 2, the lookup: a radius query is a few cell reads At this resolution, one ring means seven reads before the exact distance check. driver, within 2 km 1 cell the radius crosses borders: + 6 neighbours up to 7 reads 7 cell IDs, maybe 7 different pages. 7 cell IDs, then check candidate distances.
At a resolution where one neighboring ring covers 2 km, the query reads the driver's cell and its six adjacent cells. Those seven cell IDs can map to seven different pages, yielding up to seven reads. The system then checks exact distances for the candidate food-delivery orders. The number of cell lookups stays constant as the table grows from ten thousand orders to ten million; a larger search radius just reads a few more rings.
Notes ↗
M2

Compression Basics: Making Data Smaller

Columnar compression (run-length, dictionary, delta) exploits repetition inside one column, typically shrinking on-disk size 10× thereby cutting IO by the same factor.

Four columnar compression techniques at a glance Four panels side by side, one per technique. RLE turns a run like Mickey times three, Daffy times four into one value plus a count, 10 to 100 times smaller, very fast, best for sorted repeated values. Dictionary replaces low-cardinality strings with small integer codes, 5 to 20 times, fast, best for low-cardinality strings. Delta stores the difference between sorted numbers, 5 to 10 times, fast, best for sorted numbers and timestamps. Bit packing stores small integers in the minimum bits, 4 to 8 times, very fast, best for small integers and flags. In each panel the raw input is grey and the encoded result is orange. Color key: orange is the encoded result, grey is the raw input. Four ways to shrink a column Same idea: a column repeats, so stop repeating it. Pick by the data. RLE RAW Mickey,Mickey,Mickey, Daffy,Daffy,Daffy,Daffy ENCODED (Mickey, 3) (Daffy, 4) Ratio 10–100× Speed very fast BEST FOR Sorted, repeated values Dictionary RAW rock, pop, rock, jazz ENCODED 0, 1, 0, 2 + dict {rock:0, pop:1, jazz:2} Ratio 5–20× Speed fast BEST FOR Low-cardinality strings Delta RAW 1700000000, 1700000001, 1700000003, 1700000004 ENCODED 1700000000, +1, +2, +1 Ratio 5–10× Speed fast BEST FOR Sorted numbers, timestamps Bit packing RAW 5 3 4 1 (32-bit ints) ENCODED 101 011 100 001 3 bits each Ratio 4–8× Speed very fast BEST FOR Small integers, flags
We can compress a column by removing repeated structure. RLE stores each run as a value plus a count (i.e., length of a run of Mickeys). Dictionary encoding maps each distinct value to a small integer code. Delta encoding stores differences between adjacent sorted numbers. Bit packing stores small integers in the minimum number of bits. The raw input is grey, the encoded result orange. Ratios run from about 4x (bit packing) to 100x (RLE on sorted data); choose based on the column's value distribution.
Notes ↗
Stacking compression: 4 MB down to 50 KB One user_id column of 1 million values, 4 MB raw, sorted by user_id with about 25,000 active users, shrunk in three stacked steps, drawn as a descending staircase of orange boxes whose height tracks the size. RLE collapses the sorted runs to 200 KB. Dictionary shrinks each value because 25,000 ids need only a 2-byte code, not a 4-byte int, to 100 KB. zstd is a general-purpose codec that entropy-codes the leftover byte patterns, to 50 KB. The end result is 98.75 percent smaller. Color key: orange is the column shrinking, grey is the step applied. Stack the techniques and the column collapses 1M user_id values, sorted by user_id, ~25K users. Box height tracks size, not to scale. Raw user_id 4 MB 1M × 4 bytes RLE After RLE 200 KB sorted → long runs dictionary After dict 100 KB 25K ids → 2-byte codes zstd After zstd 50 KB 4 MB → 50 KB 98.75% smaller
A 1M-row user_id column sorted by user_id with ~25,000 active users compresses by chaining encodings. RLE collapses the sorted runs (4 MB to 200 KB). Dictionary encoding then replaces ids with codes; 25,000 ids fit in a 2-byte code rather than a 4-byte int (to 100 KB). zstd entropy-codes the remaining byte patterns (to 50 KB), a 98.75% reduction. Unsorted data or high cardinality is less compressible.
Notes ↗
M2

Bloom Filters

A Bloom filter stores a set in a bit array and uses k hash functions to map each item to k bit positions.

Bloom filter: insert alice, look up alice, look up bobA 16-bit Bloom filter with k = 3 hash functions; bits 9 and 11 were set by earlier inserts. 1, INSERT "alice" hashes to bits 3, 7 and 14 and sets them. 2, LOOKUP "alice" checks bits 3, 7 and 14; all are 1, so alice is probably in the set, which could be a false positive because other items set bits too. 3, LOOKUP "bob" checks bits 3, 9 and 2; bit 2 is 0, so bob is definitely not in the set: one 0 is proof, so there are no false negatives. Color key: orange is a set bit, grey an unset bit, green is a guaranteed answer.0[0]0[1]0[2]0[3]0[4]0[5]0[6]0[7]0[8]1[9]0[10]1[11]0[12]0[13]0[14]0[15]1111INSERT "alice"h1("alice") % 16 = 3 → 1h2("alice") % 16 = 7 → 1h3("alice") % 16 = 14 → 1Sets bits 3, 7, 14.The row above turns them on.2LOOKUP "alice"h1("alice") % 16 = 3 → 1h2("alice") % 16 = 7 → 1h3("alice") % 16 = 14 → 1All 1 ⇒ probably in the setCould be a false positive:other items set bits too.3LOOKUP "bob"h1("bob") % 16 = 3 → 1h2("bob") % 16 = 9 → 1h3("bob") % 16 = 2 → 0A 0 ⇒ definitely NOT in the setOne 0 is proof, so thereare no false negatives.a set bita guaranteed answer
A Bloom filter stores a 16-bit array and k = 3 hash functions; earlier inserts set bits 9 and 11. (1) INSERT "alice" hashes to bits 3, 7, and 14 and sets them. (2) LOOKUP "alice" checks the same three bits: all are 1, so "alice" is probably in the set. All 1s can also be a false positive, because other items set bits too. (3) LOOKUP "bob" checks bits 3, 9, and 2: bit[2] = 0 guarantees "bob" is not in the set (green). Any 0 means "definitely not in the set", so false negatives never occur. A bigger bit array lowers the false-positive rate at the cost of space; k is tuned to the array size and item count. In general, a Bloom filter checks membership in RAM before paying an expensive IO.
Notes ↗
M2

Case Study 2.2: How early Chrome Safe Browsing used a Bloom filter

Chrome Safe Browsing checks if a user is visiting a phishing site.

Early Chrome Safe Browsing: a local Bloom filter and a prefix check A worked early Safe Browsing design. A dashed boundary separates the device from Google. The device holds a 125 MB Bloom filter estimated from an 8 GB raw list, a 64-times reduction. A definite miss ends locally. A possible match sends a 32-bit hash prefix to Google, which returns the full hashes sharing that prefix so the device can finish the check. Early Safe Browsing: check on-device, confirm possible matches Worked estimate: a 125 MB Bloom filter derived from an 8 GB raw list. privacy boundary YOUR DEVICE — Chrome GOOGLE SAFE BROWSING Bloom filter · 125 MB Worked 8 GB raw list, 64× smaller Illustrative raw list 100M URLs · ~8 GB google.com DEFINITE MISS Answered on-device. Nothing sent to Google. phish-bank.ru/login MAYBE 32-bit hash prefix only hashes sharing that prefix BLOCKED Google only saw the prefix, never your URL. a definite miss, answered on-devicea confirmed threat
This example starts with 100 million URLs averaging 80 bytes, or 8 GB. At 10 bits per item, a Bloom filter occupies about 125 MB, 64 times less space. A definite miss ends locally. A possible match sends a 32-bit hash prefix to Google, which returns the matching full hashes so the device can finish the check. The server receives a prefix instead of the full URL.
Notes ↗
M2

Locality-Sensitive Hashing and Vector Embeddings

An ordinary hash spreads similar inputs across different buckets.

An ordinary hash scatters similar items Two near-identical cat vectors feed an ordinary hash, which is built to spread inputs out, so they land in unrelated buckets, bucket 7 and bucket 3, far apart in the bucket row. A red bar states the consequence: similar inputs land in unrelated buckets, so finding similar items would have to scan every bucket. This is the problem locality-sensitive hashing fixes. Color key: orange marks the two similar items, grey the hash and buckets, red the two scattered destinations and the verdict. cat A [0.81, 0.79, ...] cat B [0.80, 0.79, ...] near-identical hash( ) buckets 0 1 2 3 4 5 6 7 8 9 cat A → 7,  cat B → 3 Similar items, unrelated buckets: "find similar" would have to scan every bucket. the two similar itemswhere they scatter
An ordinary hash spreads inputs across buckets. Two near-identical vectors (cat A and cat B) land in unrelated buckets, 7 and 3. The bucket index carries no information about distance, so a similarity query still scans every bucket. Locality-sensitive hashing groups near vectors instead.
Notes ↗
LSH: similar items hash to the same bucket Four items become feature vectors, then k random hyperplanes turn each vector into a sign-bit code, then a bucket. Cat 1 and Cat 2 are similar so both get code 1011 and bucket 11 (orange, the focus pair); Dog gets bucket 9 and Car bucket 4 (grey). A query for items similar to Cat scans only bucket 11. Color key: orange is the similar pair in focus, grey is the other items. LSH: similar items land in the same bucket k random hyperplanes turn each vector into a sign-bit code, then a bucket Item Cat 1 Cat 2 Dog Car Feature vector → → → → [0.8, 0.7, 0.9, ...] [0.7, 0.8, 0.8, ...] [0.3, 0.9, 0.2, ...] [0.1, 0.0, 0.5, ...] k-bit LSH code → → → → 1011 1011 1001 0100 Bucket → → → → Bucket 11 Bucket 11 Bucket 9 Bucket 4 Query “find items similar to Cat”:  scan only bucket 11 → find Cat 2; never compare against Dog or Car
Each item becomes a feature vector, an embedding, then a short LSH code, then a bucket. The two similar cats receive code 1011 and share a bucket, while the dog and car map to different buckets. A query for items like the cat reads that one bucket instead of comparing against every stored vector. Code bits come from random cuts of the space.
Notes ↗
Each random cut adds one bit to the code A space crossed by three dashed cuts. Above a cut implies 0, below implies 1, so three cuts give each point a three-bit code. The two cat photos read 111 because no cut falls between them, and share a bucket. The dog reads 011 and the car reads 110. The dog differs on cut 1, the car on cut 3. A table on the left fills in above or below for each point as every cut lands. Cat 1 Cat 2 Dog Car Which side of each cut? cut 1 cut 2 cut 3 Cat 1 Cat 2 Dog Car above = 0, below = 1 cut 1 below 1·· below 1·· above 0·· below 1·· cut 2 below 11· below 11· below 01· below 11· cut 3 below 111 below 111 below 011 above 110 The two cats match on all three cuts. The dog differs on cut 1, the car on cut 3. Equal codes go in the same bucket. No distance is ever computed.
Each cut asks one yes-or-no question: above the line is 0, below is 1, and the code assembles one bit at a time. The same four items as Figure 2. The two cats end on 111 because no cut fell between them, so they share a bucket; the dog differs on cut 1 and the car on cut 3, so each lands elsewhere. Nothing measures a distance, so a nearness lookup becomes an ordinary hash-table lookup: cost O(N) drops to about O(N / 2k). Misses happen when a cut does fall between a close pair.
Notes ↗
The cut is always one dimension less than the space A cloud of 200 unit vectors in three dimensions, holding the two near-duplicate cats and the unrelated dog. Planes through the origin arrive one at a time and orange marks every vector still on Cat 1’s side of all of them. After the first plane the dog is still in Cat 1’s bucket, because one cut only halves the space; the second plane separates it, and Cat 2 stays throughout. The candidate set falls from 200 to 94, then 43, then 19. A ladder along the bottom names the same object in each dimension: in two dimensions the cut is a line, in three a plane, and in 256 a hyperplane, always one dimension less than the space it cuts. 200 vectors indexedNo cuts yetCat 1Cat 2 near-duplicate of Cat 1Dog unrelated200of 200 share one bucket: no cut yet 200 vectors, no cuts yet d = 2 the cut is a line 1-dimensional d = 3 the cut is a plane 2-dimensional ← you are here d = 256 the cut is a hyperplane 255-dimensional A cut is always one dimension below the space it splits Drawn here at d = 3, where the cut is an ordinary plane. 200 vectors indexed1 cut · k = 1 of 3 · d = 3Cat 1 0Cat 2 0 same bucketDog 0 still in Cat 1’s bucket94of 200 still in Cat 1’s bucket one plane halves the space d = 2 the cut is a line 1-dimensional d = 3 the cut is a plane 2-dimensional ← you are here d = 256 the cut is a hyperplane 255-dimensional A cut is always one dimension below the space it splits Drawn here at d = 3, where the cut is an ordinary plane. 200 vectors indexed2 cuts · k = 2 of 3 · d = 3Cat 1 00Cat 2 00 same bucketDog 01 different bucket43of 200 still in Cat 1’s bucket the second plane puts the dog in a different bucket d = 2 the cut is a line 1-dimensional d = 3 the cut is a plane 2-dimensional ← you are here d = 256 the cut is a hyperplane 255-dimensional A cut is always one dimension below the space it splits Drawn here at d = 3, where the cut is an ordinary plane. 200 vectors indexed3 cuts · k = 3 of 3 · d = 3Cat 1 000Cat 2 000 same bucketDog different bucket19of 200 still in Cat 1’s bucket three planes: 19 of 200 still share Cat 1's bucket, Cat 2 among them d = 2 the cut is a line 1-dimensional d = 3 the cut is a plane 2-dimensional ← you are here d = 256 the cut is a hyperplane 255-dimensional
In 2D that is a line, which is what Figure 3 drew; in 3D it is an ordinary plane, drawn here; in an embedding’s few hundred dimensions it is a hyperplane (hard to picture). Nothing else changes: above or below is still the only question. One cut is insufficient. After the first plane the dog is still in the cat’s bucket, because one cut halves the space and a coin flip put it on that side. The second ejects it, and the near-duplicate survives all three. Each plane roughly halves the candidates: 200, then 94, then 43, then 19.
Notes ↗
M2

Case Study 2.3: How OpenAI and Meta's FAISS Speed Up Vector Search

OpenAI's embeddings are vectors.

The exact scan: one query against every vectorExact L2 search computes one distance from the query, a 1,536-D vector, to each of N = 1,000,000 stored vectors, every one to the last. One query costs 1,000,000 L2 distances, about 1.5 billion coordinates and 6.1 GB of vector data, repeated for every query: exact search performs O(N) distance evaluations per query. Color key: orange is the query, grey the stored vectors and the scan.querya 1,536-D vectorN = 1,000,000 stored vectorsevery one, to the lastone 1,536-D distanceto each stored vectorCost of one query1,000,000 L2 distances≈ 1.5 billion coordinates≈ 6.1 GB of vector datarepeated for every queryExact search performs O(N) distance evaluations per query.
Exact scan against all N vectors. At one million vectors, a query computes one million 1,536-dimensional L2 distances, covering about 1.5 billion coordinate values and 6.1 GB of vector data. In FAISS, IndexFlatL2 performs this exact search.
Notes ↗
HNSW is built in layers A hierarchical proximity graph. Layer 0 holds every point with selected nearby links. Random level assignment places fewer points in each upper layer, where links span longer distances. Dashed verticals mark the same point across layers. Color key: orange is points and graph links, grey dashed is the same point across layers. HNSW is built in layers Every point sits on the ground floor; a random few are promoted higher. Layer 0 Layer 0: every point, linked to its nearest neighbors. Layer 1 Layer 2 The promoted few form the long links up top.
Each vector is assigned a random height, so only a few rise to the upper layers. At the bottom, nearly all of them connect to their neighbors like local streets. The few promoted higher become the sparse freeway stops the search starts from.
Notes ↗
Brute-force scan vs an ANN indexOne query over a million 1,536-D embeddings. Exact search with IndexFlatL2 compares the query to every vector: one million 1,536-D L2 distances, about 6.1 GB read, an exact answer, O(N) work per query. An ANN index (IVF or HNSW) looks at a small fraction: IVF probes a few clusters, HNSW walks a graph to the neighbourhood. It examines fewer candidates and may miss a neighbour. Color key: orange is the ANN index.Exact search (IndexFlatL2)Compare the query to every vector• 1M × 1,536-D L2 distances (~6.1 GB read)• Exact answer• O(N) work per queryindexANN index (IVF or HNSW)Look at a small fraction• IVF: probe a few clusters• HNSW: walk a graph to the neighbourhood• Fewer candidates; may miss a neighbour
Exact search compares the query with all one million vectors. An ANN index checks only a slice of them, so it answers far faster but can skip the true closest. Check more candidates and you trade speed for accuracy.
Notes ↗
M2

Case Study 2.4: Compressing a Billion Embeddings

A float32 vector index stores more precision than search needs.

The demo fit in RAM; production does notThe index has to sit in RAM to search fast. The raw float32 index, about 60 GB for 10 million 1,536-D vectors at 4 bytes each (about 6 KB per vector), does not fit in the machine's RAM. Quantizing, 8 to 32 times smaller, gives the same index at a few bits per number, which fits with room to spare. Color key: grey is the machine's RAM, red is float32 that does not fit, green is quantized and fits.RAMraw float32 indexRAMthe same index, quantized~60 GB10 M × 1,536-D × 4 B≈ 6 KB per vectordoes not fitquantize8–32× smallerquantized indexa few bits per numberfits, with room to sparefloat32, does not fitquantized, fits
A full-precision embedding is big. Most have about a thousand dimensions or more. OpenAI's smaller embedding model is 1,536-D and the large one is 3,072-D. At 1,536-D float32 that is about 6 KB per vector, so 10 million vectors take ~60 GB in RAM for fast search. The large model uses double. Quantizing each coordinate to a few bits shrinks the index 8 to 32 times and brings it under the RAM limit.
Notes ↗
A random spin turns the grid, not the points One panel. San Francisco, Berkeley and Stanford stay at the same coordinates in every frame while the grid behind them rotates. SF and Berkeley begin in one cell and end in different cells after a spin of 58 degrees; Stanford, much further away, is unaffected. A read-out lists the cell each city falls in. SF Berkeley Stanford SF (-1, -1) Berkeley (-1, -1) Stanford (0, 0) SF and Berkeley share a cell rotation 0° The points never move. Only the grid turns. SF Berkeley Stanford SF (-1, -1) Berkeley (-1, -1) Stanford (0, 0) SF and Berkeley share a cell rotation 14° The points never move. Only the grid turns. SF Berkeley Stanford SF (-1, -1) Berkeley (-1, -1) Stanford (0, 0) SF and Berkeley share a cell rotation 29° The points never move. Only the grid turns. SF Berkeley Stanford SF (-1, -1) Berkeley (-1, -1) Stanford (0, 0) SF and Berkeley share a cell rotation 44° The points never move. Only the grid turns. SF Berkeley Stanford SF (-1, 0) Berkeley (-1, -1) Stanford (0, 0) SF and Berkeley are now in different cells rotation 58° The points never move. Only the grid turns.
Eight frames from 0° to 58°. The three cities sit at identical coordinates in every frame, so you can check for yourself that only the grid moved. San Francisco and Berkeley begin in one cell and end in different ones. Stanford, much further off, is untouched.
Notes ↗
Four bits buys 16 levels. Before the rotation, 5 get used. 256 embedding coordinates drawn as bars, beside a histogram of how many fall into each of the 16 levels a 4-bit quantiser offers. Before the rotation the coordinates are dominated by a few large ones, so only 5 of the 16 levels are ever used and one level holds most of them. After a random rotation the coordinates are evenly spread and 15 of the 16 levels are in use. Storage is 4 bits per coordinate in both cases. 256 COORDINATES THE 16 LEVELS THEY ROUND INTO one shared axis: read straight across from a bar to its slot empty empty empty empty empty empty empty empty empty empty empty levels holding any data 5 of 16 levels sitting empty 11 error at the same 4 bits 38% Every coordinate costs 4 bits to store either way. What changes is how many of the 16 levels do any work. 256 COORDINATES THE 16 LEVELS THEY ROUND INTO one shared axis: read straight across from a bar to its slot empty empty empty empty empty empty empty empty empty levels holding any data 7 of 16 levels sitting empty 9 error at the same 4 bits 33% Every coordinate costs 4 bits to store either way. What changes is how many of the 16 levels do any work. 256 COORDINATES THE 16 LEVELS THEY ROUND INTO one shared axis: read straight across from a bar to its slot empty empty levels holding any data 14 of 16 levels sitting empty 2 error at the same 4 bits 14% Every coordinate costs 4 bits to store either way. What changes is how many of the 16 levels do any work. 256 COORDINATES THE 16 LEVELS THEY ROUND INTO one shared axis: read straight across from a bar to its slot empty levels holding any data 15 of 16 levels sitting empty 1 error at the same 4 bits 11% Every coordinate costs 4 bits to store either way. What changes is how many of the 16 levels do any work.
The same idea at 256 dimensions. A 4-bit budget is 16 levels. Before the rotation the coordinates are so uneven that only 5 levels are ever used, which is 2.3 bits of information in the 4 bits you are paying to store. After it, 15 levels are in play at 3.9 bits. Storage is 4 bits per coordinate either way; only the useful fraction changes.
Notes ↗
Drop the precision, keep the distanceA coordinate stored today as 0.4327 uses 32 bits per number, the exact coordinate. After quantizing it is stored as 10: 2 bits, bucket 2 of 4, which decodes back to about 0.4. The distance between two vectors barely moves, because after the spin no single coordinate carries the answer. Sixteen times smaller, and no codebook to train. Color key: orange is the quantized value.As stored today0.432732 bits per numberthe exact coordinatekeep fewer bitsAfter quantizing102 bits · bucket 2 of 4decodes back to about 0.4The distance between two vectors barely moves, because after the spin no single coordinate carries the answer.Sixteen times smaller, and no codebook to train.
Scalar quantization of one coordinate. 0.4327 stored in 32 bits maps to a 2-bit bucket 10 and decodes to about 0.4. One coordinate is now rough, but a distance adds up hundreds of them, and the rounding errors mostly cancel out. The rotation first spreads the values evenly, so one simple quantizer fits every coordinate and every dataset, with no training. Two bits gives 16× compression, and one bit gives 32×.
Notes ↗
M2

Practice Problems for Basic Systems Design

Two levers for a too-big problem: partition the lookup, or shrink each itemEvery structure here turns a big problem into a small one. Lever 1, don't scan all N: partition the lookup so a query touches one slice; exactly by hashing to one bucket (Basic Hashing), or approximately by bucketing similar items or pruning what is definitely absent (Bloom, LSH, IVF, HNSW). Lever 2, don't store all N bytes: shrink each item so the index fits; losslessly by dropping redundancy (RLE, dictionary, delta, bit-pack), or lossily by dropping precision while keeping the order (PQ, TurboQuant). Stack both and you get, for example, IVF-PQ, the vector database. Color key: orange is the lever.1 · Don't scan all NPartition the lookup, so a query touches one sliceEXACTHash to one bucketPartition by key; read one bucket, not N.HashingAPPROXIMATEBucket by similarity, or prune earlyBucket similar items, or prune what is definitelyabsent; check only the candidates.Bloom · LSH · IVF · HNSW2 · Don't store all N bytesShrink each item, so the index fitsEXACT · LOSSLESSDrop the redundancyExploit the structure the data already has.RLE · dictionary · delta · bit-packAPPROXIMATE · LOSSYDrop the precision, keep the orderReframe with a random rotation so one coarsequantizer fits any data.PQ · TurboQuantStack both, and you get, for example, IVF-PQ: the vector database.
Two levers for a too-big problem. Lever 1 partitions the lookup so a query examines one slice instead of all N: exactly, by hashing to a bucket; approximately, by pruning what is definitely absent (Bloom) or bucketing by similarity (LSH, IVF, HNSW) and checking only the candidates near the query. Lever 2 shrinks each item: losslessly by dropping the redundancy the data already has (only if it has structure), or lossily by dropping precision while keeping the rank order (a random rotation reframes the data so one coarse quantizer fits any of it). Each lever has an exact form and an approximate one. Real systems pull both at once: a vector database like IVF-PQ partitions the data and then compresses what is left.
Practiceall 11 problems, with solutions ↗Notes ↗
end of lecture 1~62 min of figures
M2 Systems basics 1 of 28
▶ Y Notes □ keys

M2 Systems basics

Disks, hashing, bloom filters, compression, vectors · 28 figures · ~62 min · 1 lectures at this pace