SD System Design In Depth
0%
lesson system-designgeospatialdistance

Nearby geospatial search

Module 5.3: Geospatial Indexing & Proximity Search Updated Sep 18, 2026
📄 Cheat Sheet
FAANG System Design Interview: Design A Location Based Service (Yelp, Google Places)
ByteByteGo FAANG System Design Interview: Design A Location Based Service (Yelp, Google Places) Watch on YouTube ↗

The canonical proximity-service walkthrough: naive scan, geohash, quadtree and S2 compared, then the full read-heavy architecture with caching and replicas.

0:00 / --:--

Understanding Nearby Geospatial Search

Geospatial search is a core component of many modern applications, from ride-sharing services like Uber to review platforms like Yelp. At its core, the problem is about finding points of interest (POIs) that are physically close to a user's current location within a given radius.

The Naive Approach

A naive approach might involve storing the latitude and longitude of every POI in a relational database and calculating the distance between the user and every POI on the fly. You could use the Haversine formula for this:

SELECT * FROM places WHERE haversine(lat, lon, user_lat, user_lon) < radius;

However, this requires a full table scan, computing a complex trigonometric function for millions of rows. It is highly inefficient and will not scale.

Spatial Indexing

To optimize this, we need spatial indexing. The fundamental idea of spatial indexing is to map two-dimensional coordinates (latitude and longitude) into one-dimensional strings or numbers, which can then be efficiently indexed by standard databases (using B-Trees, for example). Common strategies include:

  • Geohash: Divides the world into a grid of rectangles, assigning a string to each.
  • Quadtree: Recursively subdivides space into four quadrants based on POI density.
  • Google S2 / Uber H3: Uses spherical projections to map the Earth into cells.

Database Support

Many modern databases support spatial queries out of the box using extensions:

  • PostGIS: An extension for PostgreSQL that adds support for geographic objects.
  • Redis GEO: Built-in commands like GEOSEARCH for ultra-fast, in-memory proximity queries based on Geohash.
  • Elasticsearch: Provides geo-point and geo-shape data types for powerful search capabilities.

Trade-offs in Geospatial Design

Concrete query: for couriers within 3 km, first use a spatial index to fetch a bounded candidate set whose cells or bounding boxes cover the circle, then exact-filter by geodesic distance, availability, tenant/permission, and a location freshness cutoff before sorting by ETA. A courier last updated five minutes ago is not made eligible by being geometrically close. Cap candidates and state whether an incomplete shard returns a partial result or an error.

Static POIs tolerate slower writes and aggressive caching. Moving couriers need frequent index updates and stale-member removal, but an in-memory index adds rebuild and failover work. Geographic partitioning reduces the working set while creating boundary queries, hot-city imbalance, and handoff work when an object crosses regions.

flowchart TD User["User App"] --> API["API Gateway"] API --> SearchService["Search Service"] SearchService --> Cache["Redis GEO Cache"] SearchService --> DB[("PostGIS Database")]

Worked example: sizing a covering

A user in San Francisco (latitude about 37.8) searches a 2 km radius. Geohash cells are fixed in degrees, so their width in kilometres shrinks with the cosine of latitude.

  • Precision 5 (0.0439 degrees of longitude by 0.0439 of latitude): about 3.9 km wide by 4.9 km tall here. The 4 km wide circle fits inside the center cell plus its 8 neighbours: 9 cells covering about 171 square km, while the circle is only 12.6 square km, so roughly 7 percent of candidates are real hits.
  • Precision 6 (about 0.98 km by 0.61 km here): the circle needs about 6 x 8 = 48 cells covering about 29 square km, so about 44 percent are hits, at the price of 48 key lookups.
  • At a density of 500 places per square km, precision 5 returns about 85,000 candidates and precision 6 about 14,000, of which about 6,300 are inside 2 km. Rank by relevance and distance and return the top 20. For very dense areas, cap candidates or shrink the radius.

Technology choices

OptionBest forStrengthWeakness
PostGIS with a GiST indexStatic POIs with rich filters and joinsExact geography types, SQL, polygonsWrite-heavy moving objects; single-node scaling limits
Elasticsearch or OpenSearch geo_pointSearch combined with text and facetsDistance filters plus relevance scoringRefresh lag; operational weight
Redis GEOHot, frequently updated positionsIn-memory, simple commandsOne key per shard; no per-member expiry
In-memory quadtree or grid serviceCustom, very high QPSAdapts to density, full controlYou own rebuilds, replication and failover
H3 or S2 cell IDs in any key-value storeGlobal scale, sharding by cellUniform cell hierarchy, easy partitioningStill needs exact filtering and coverings

Scaling the read path

  • POI data changes rarely, so a proximity service is read-heavy: keep the index in memory on stateless replicas and rebuild or update from a change stream.
  • Cache results per (cell, filter) for popular areas; business details are cached separately by ID.
  • Partition by region only if the dataset outgrows one machine; 200 million POIs at about 50 bytes of index entry each is about 10 GB, which still fits in memory on one large node.

How real systems do it

  • Yelp has written about using Elasticsearch for location-aware search.
  • Uber open-sourced H3 and indexes marketplace data by hexagonal cell.
  • Tinder described geosharding its recommendation index by S2 cells so each query touches only a few shards.
Check yourself Question 1 of 3
Candidates are not answers recall
From: Understanding Nearby Geospatial Search

A courier search for "within 3 km" fetches candidates from the spatial index cells covering the circle. One courier sits inside a covered cell, 2.1 km away, but last reported location five minutes ago. According to the lesson, what should happen to that courier?

Geohash Explorer
Proximity Search
Click map to drop pin
Lat: -
Lon: -
Geohash (Prec 5): -

Key Takeaways

Naive distance calculations do not scale.
Spatial indexing converts 2D coordinates into 1D keys.
PostGIS and Redis are excellent choices for geospatial workloads.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue