System Design Interview

Course Content

System Design Interview

31 sections · 71 lessons

Proximity Service: scope, scale and why a naive query fails


The prompt: "Design a service that finds nearby places." Open an app, and it shows the restaurants, shops, or petrol stations within a few kilometres of where you are standing.

Attempt it for 45 minutes before reading. Write down the database query you would issue first, then work out how many rows it touches. That calculation, done honestly, is what makes the rest of the design necessary.

This lesson does that calculation. It scopes the problem, puts numbers on reads, writes and storage, and then tries the ordinary database tools and measures how badly they fail. The failure is what tells you what a spatial index has to do.

Five questions about nearbyProximity scopeFixed or user radius?How many places?Read or write heavy?Filter by open now?Rank beyond distance?
Whether the radius is fixed decides the index, because a fixed radius maps onto one grid resolution.

Why this section is foundational

Section 19 (Nearby Friends) and Section 20 (Google Maps) both build directly on this one. This is where the vocabulary of geospatial indexing is established — grids, geohashes, quadtrees — and the two later sections assume it. Section 19 in particular exists to show where this design stops working, so it is worth being clear here about the assumptions that make it work.

The most important of those assumptions is that the data barely moves. A restaurant's location is fixed. That single property is what makes a precomputed index viable, and it is exactly the property Section 19 removes.

The five questions

1. Is the search radius fixed or user-selected? A fixed radius (always 5 km) lets you choose one index granularity and stop thinking about it. A user-selected radius from 500 m to 50 km means the index must answer at several scales, which favours some structures over others.

2. How many places, and how often do they change? Ask for both. Two hundred million places that change a few thousand times a day is a completely different problem from two hundred million points that each move every 30 seconds — which is the Nearby Friends problem in Section 19.

3. What is the latency target? Nearby search sits in front of a user waiting on a map screen. Around 100 ms of server time is a reasonable target, and it rules out anything that scans a large fraction of the data.

4. Ranked by distance only, or by other signals? Distance-only is a pure geometry problem. Distance plus rating, plus whether the place is open, plus paid placement, is a retrieval problem followed by a ranking problem — and separating those two phases is the right structure either way.

5. Is business data in scope, or only coordinates? Scope the place details — name, hours, photos — as a separate store, because it is an ordinary key-value lookup and mixing it into the geospatial discussion is a distraction.

Requirements and scale

Functional: given a latitude, longitude, and radius, return the places within it; return details for a place; add, update, and remove places.

A read-heavy index that barely changes200 M worldwideFits on one node100 M usersRegional replicas5 K per secondCacheable by cellabout 200 GBMemory-residentRare editsReplicate,not shardNumberConsequencePlacesDaily usersPeak searchesIndex sizeWrite rate
Two hundred gigabytes that hardly changes is a replication problem, not a sharding problem, and that decides the deployment.

Non-functional: 99th-percentile search latency under 100 ms server-side; high availability for search, since a map screen with no results is a broken product; the index may be minutes stale, because a new café appearing in results a few minutes late harms nobody.

The numbers

Invented figures for a global places service. A day is 100,000 seconds (see Rounding aggressively and staying fast).

QuantityAssumption
Places in the dataset200 million
Daily active users100 million
Nearby searches per user per day5
Place updates per day10,000

Search rate:

100M × 5 = 500 million searches/day

500M ÷ 100,000 s = 5,000 searches/s average, and at a 4× peak, 20,000/s

The asymmetry:

500,000,000 reads ÷ 10,000 writes = 50,000 reads per write

That ratio is the licence to precompute. An index that takes an hour to build and answers in a millisecond is an excellent trade when it is read half a billion times between rebuilds. Compare with Section 19 (Nearby Friends), where every point moves and the ratio inverts.

Storage

The geospatial index itself is small. An entry is a cell identifier and a place identifier: call it 8 bytes plus 8 bytes, with overhead, 24 bytes.

200M places × 24 bytes = about 5 GB

Five gigabytes fits in memory on a single machine. This is the most important storage fact in the design, and it shapes Serving and follow-ups: the index does not need sharding for capacity, so it can be replicated instead, which is far simpler and fails better.

Place details are separate and larger. Name, address, hours, category, photos references, rating: perhaps 2 KB per place.

200M × 2 KB = 400 GB

Ordinary key-value data, partitioned by place identifier, cached by identifier. It never participates in a geospatial query — the geo index returns identifiers, and a second lookup hydrates them. Keeping those two stores separate is a design decision worth stating.

Bandwidth

A response of 20 nearby places with summary details is perhaps 20 KB. At 20,000 searches per second that is 400 MB/s of response traffic — real, but unremarkable, and reducible by returning identifiers plus a thin summary and letting the client fetch detail on tap.

Why a naive query fails

Before reaching for a spatial index, establish that the ordinary tools fail. Assert it and the interviewer has no reason to believe you; measure it and the rest of the design is inevitable.

Both ordinary tools fail, differentlyDistance to every row• 200 M haversine calls per query• Seconds per request, not milliseconds• No index helps a computed columnSeparate lat and long indexes• Each index returns a huge band• The database intersects two big sets• Still scans millions of candidates
Measuring the failure rather than asserting it is what makes the geospatial index feel inevitable.

Attempt one: compute distance to everything

SQL
SELECT place_id, name FROM placesWHERE haversine(lat, lng, :user_lat, :user_lng) < 5000ORDER BY 3 LIMIT 20;

This is correct and unusable. The distance is computed from the row's own columns, so no index can help: the database must read all 200 million rows and evaluate a trigonometric function on each.

Cost it. Even at an optimistic 5 million rows per second per core of scan-and-compute:

200,000,000 ÷ 5,000,000 = 40 seconds of CPU per query

Against a 100 ms budget, that is 400× over. At 5,000 queries per second it would need 200,000 cores doing nothing else. The failure is not marginal — it is three orders of magnitude.

Attempt two: index latitude and longitude

The instinct is to add a bounding box so an index can be used:

SQL
WHERE lat BETWEEN :lat-0.05 AND :lat+0.05  AND lng BETWEEN :lng-0.05 AND :lng+0.05

This is better and still fails, for a reason worth understanding properly.

Count the rows. A band of ±0.05° of latitude spans roughly 11 km. If the 200 million places were spread evenly across 180° of latitude:

200M × (0.1 ÷ 180) = about 111,000 rows in the latitude band

The longitude band similarly holds around 55,000 rows. The database retrieves one of those sets — over a hundred thousand rows — and filters it row by row. The final answer is perhaps 200 places.

111,000 rows read to return 200 = 555 rows read per useful row

And that is the uniform-distribution case, which is the optimistic one. Places cluster in cities. A latitude band through a dense metropolitan region contains far more than the average, so the query is slowest exactly where users are densest.

At 5,000 queries per second, 111,000 row reads each is 550 million row reads per second. No amount of replication makes that reasonable.

What the failure tells you

The problem is dimensional. Two coordinates describe a point on a plane, and a one-dimensional ordering cannot preserve two-dimensional closeness. Every technique in Geospatial indexing options is a different answer to the same question: how do you map two dimensions onto one, so that an ordinary index can find neighbours?