SystemDesign.website
Track 3 ยท Module 3.7 ยท DataStores & Storage Engines

Geospatial Databases & Spatial Indexing

How do apps like Uber, Yelp, Google Maps, and Tinder find drivers or restaurants within 2 km in less than 5 milliseconds? Learn why 2D maps break traditional databases, and master Geohashes, QuadTrees, and Google S2 from first principles.

โฑ๏ธ Reading Time: ~25 min
๐ŸŽฏ Real Systems: Uber Driver Dispatch, Yelp Nearby, Tinder Discovery
๐ŸŽฌ Format: Beginner Friendly + Everyday Analogies + Live Lab

๐Ÿ—บ๏ธ 1. Why Normal Databases Can't Search Maps

๐Ÿ“– The Simple Analogy: A Dictionary vs A Treasure Map

Think of a traditional database index (like a B-Tree) as an alphabetical dictionary.

If you search for words starting with "Cat", the dictionary is lightning fast because words are arranged along a single, sorted 1D line (from A to Z).

But what happens when you ask a dictionary: "Find all words located within 2 miles of Central Park"?The dictionary is completely useless! It only knows how to sort in 1 dimension (alphabetical), but the real world has 2 dimensions:

1. Latitude (North โ†• South)

Measures how far North or South you are from the Equator.

2. Longitude (West โ†” East)

Measures how far East or West you are from the Prime Meridian.

โŒ Why a regular SQL query is slow:SELECT * FROM restaurants
WHERE lat BETWEEN 37.70 AND 37.80 AND lng BETWEEN -122.50 AND -122.40;

If you index latitude, the database searches a narrow horizontal band that wraps around the entire planet! It looks through millions of restaurants in Tokyo, Seoul, San Francisco, and Athens before it can filter out the longitude.

๐Ÿ’ก The Big Goal of Spatial Indexing:We need a clever way to convert a 2D coordinate (lat, lng) into a single 1D number or string so that places that are close on the map get codes that are close in the database!

โ™Ÿ๏ธ 2. Geohash: Turning the World into a Chessboard

๐ŸŽฏ The Simple Analogy: Calling Out Battleship Coordinates

Have you ever played the game Battleship or chess? Instead of saying "My piece is 4.5 inches from the left", you say "E4".

A Geohash does the exact same thing for the whole planet. It cuts the Earth into a grid of boxes, giving each box a short code name (like "9q8yy").

How a Geohash is created (Step-by-Step):
1
Cut the map in half: Are you in the Northern or Southern hemisphere? (North = 1, South = 0).
2
Cut the other way: Are you in the Eastern or Western hemisphere? (East = 1, West = 0).
3
Repeat 20 times: Keep zooming in by cutting smaller and smaller boxes, interleaving the 0s and 1s like a zipper.
4
Encode to letters: Turn the 0s and 1s into short letters and numbers ("9q8yy").

๐Ÿ” Longer Code = Higher Zoom Level

The beauty of Geohash is that adding more letters zooms into a smaller, tighter box:

Geohash LengthSize of the BoxWhat it RepresentsExample Code
1 letter~5,000 kmAn entire continent (e.g. North America)9
4 letters~39 km ร— 20 kmA whole metropolitan area (SF Bay Area)9q8y
5 letters~4.9 km ร— 4.9 kmA city or large district9q8yy
6 letters~1.2 km ร— 0.6 kmUber & Yelp sweet spot: Local neighborhood blocks9q8yyz
8 letters~38 metersA specific building or street corner9q8yyz3p
โš ๏ธ The Edge Case: The "Apartment Hallway" Problem

Imagine you live in Apartment #199. Your friend lives in Apartment #200 right across the hallway โ€” literally 2 steps away.

Even though you are 1 meter apart, your door numbers start with different digits (1 vs 2). If a food delivery app only searches apartments starting with "1", it will completely miss your friend!

โœ… The Simple Fix: Always Search 9 Boxes!Never search only the user's current Geohash. In production, always search the user's center box + the 8 surrounding neighbor boxes (a 3ร—3 grid of 9 boxes). This guarantees you never miss a coffee shop across the street!

๐ŸŒณ 3. QuadTrees: The "Smart Zoom" Tree for Moving Cars

๐ŸŽฎ The Simple Analogy: Video Game Map Rendering

Fixed Geohash grids have one big flaw: a 1 km box in the desert has 0 cars, while a 1 km box in Times Square, New York has 25,000 cars!

A QuadTree solves this by dynamically adapting to population density:

1. The 4-Way Split Rule

Start with 1 big box for the city. If a box gets more than 100 drivers, automatically split it into 4 smaller quadrants (North-West, North-East, South-West, South-East).

2. 100% In-Memory RAM Speed

Uber drivers move every 4 seconds. Updating disk databases 1,000,000 times per second would melt the hard drives! QuadTrees live entirely in RAM for sub-millisecond updates.

๐Ÿš€ 4. Google S2: The Space-Filling Snake (64-Bit Numbers)

๐Ÿ The Simple Analogy: A Continuous Thread in a House

Imagine laying a single continuous piece of thread (a Hilbert Curve) that snakes through every room in a house without ever crossing itself. If two rooms are next to each other in the house, they are right next to each other on the thread!

Google developed Google S2 (used by Uber, Tinder, and Google Maps) which maps every spot on Earth to a clean 64-bit integer:

1. Fast 64-Bit Integers:Computers compare numbers (1029384) 10x faster than comparing text strings ("9q8yyz").
2. 1D Range Queries:Searching a 2 km circle turns into a simple number check: WHERE s2_id BETWEEN 5000 AND 5050.

โš–๏ธ 5. Cheat Sheet: Which Spatial Index to Choose?

ToolWhere it LivesBest ForReal-World Example
Geohash (Base32)Database / RedisStatic places that don't moveYelp restaurants, Airbnb homes
QuadTree100% In-Memory RAMFast moving vehicles in citiesUber / Lyft driver dispatch
Google S2RAM or Database (64-bit int)Global scale, high speedGoogle Maps, Tinder, Pokรฉmon GO
PostGIS (R-Tree)PostgreSQL DiskComplex borders and polygonsCity zoning, delivery fee boundaries

๐Ÿ’ป 6. Simple Geohash Code (Multi-Language)

Here is how easy it is to convert latitude and longitude into a Geohash string:

Select Language:
// Python 3.10+ Geohash Bisection
BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"

def encode_geohash(lat: float, lng: float, precision: int = 6) -> str:
    lat_range, lng_range = [-90.0, 90.0], [-180.0, 180.0]
    geohash, is_lng, bit, ch = [], True, 0, 0

    while len(geohash) < precision:
        if is_lng:
            mid = (lng_range[0] + lng_range[1]) / 2
            if lng > mid: ch |= (1 << (4 - bit)); lng_range[0] = mid
            else: lng_range[1] = mid
        else:
            mid = (lat_range[0] + lat_range[1]) / 2
            if lat > mid: ch |= (1 << (4 - bit)); lat_range[0] = mid
            else: lat_range[1] = mid

        is_lng = not is_lng
        bit += 1
        if bit == 5:
            geohash.append(BASE32[ch])
            bit, ch = 0, 0

    return "".join(geohash)

๐Ÿงช 7. Interactive Lab: "Find Nearby Pizza" Simulator

โšก Real-Time Proximity Lab

Interactive Geohash & 8-Neighbor Simulator

Click anywhere on the map to place the user & see the search grid!
1. Search Radius (km):
2 km
2. Boundary Neighbor Expansion:
3. Generate Random Places:
๐Ÿ“ Click on the map grid below to move the USER pin:
๐Ÿ“ Proximity Filter: Click anywhere on the map to search nearby restaurants within 2 km using Geohash prefix matching + 8 neighbors!

๐ŸŽฏ 8. Test Your Understanding

Scenario 1: The Coffee Shop Across The Street

You stand at Geohash "9q8yy". A coffee shop is 50 meters away at Geohash "9q8yw". If the SQL query runs WHERE geohash LIKE '9q8yy%', why does it return 0 results?

Scenario 2: Moving Driver Updates at Scale

Uber receives 1,000,000 driver GPS coordinates every 4 seconds. Why is storing dynamic driver locations in PostGIS with disk R-Trees impractical compared to In-Memory Google S2 / QuadTrees?

โ—‹

Completed this module?

Mark as finished to update your curriculum progress tracker.