Design Yelp / Google Maps
Low-latency spatial search indexing proximity of local business listings within user bounding boxes.
1. System Requirements
- Users search nearby businesses within a specific radius (e.g. 5 miles).
- Filter search results by rating, cuisine, or category.
- Hosts add, update, and manage listing profiles.
- Sub-100ms geospatial search response times.
- High write scalability for business listings.
2. Scale & Resource Estimates
Throughput: 10 Million business listings, 50 Million DAU. 10,000 search queries/sec.
Storage & RAM: Listing profiles: 10M * 1KB metadata = 10 GB storage, allowing complete memory-caching.
3. System API Endpoints
GET /api/v1/places/search?lat=40.7128&lon=-74.0060&radius=5000&category=restaurant
Response: { places: [ { place_id: "plc-99", name: "Luigi's Pizza", distance_meters: 250 } ] }
4. Database Schema Design
Table: places (MySQL + Spatial Extensions)
- place_id (UUID - Primary Key)
- name (VARCHAR)
- latitude (DOUBLE)
- longitude (DOUBLE)
- geohash (VARCHAR(8) - Index)
Table: reviews (MySQL - sharded by place_id)
- review_id (UUID - Primary Key)
- place_id (UUID - Foreign Key)
- rating (TINYINT)
- comment (TEXT)
5. Interactive High-Level Architecture
6. Scalability Deep Dive & Bottlenecks
Utilizes Geohashes (string representations of lat/lon coordinates using base32 characters, e.g., 'dr5reg'). Geohashes group close geographic regions into logical prefixes. The proximity search queries the business catalog matching the user's geohash prefix using Redis Geohash indices (`GEORADIUS`). A Quadtree index is held in-memory to dynamically adjust grid sizes in dense city areas.
7. Production Scaling Strategies
Geohash Precision Tuning: Use precision 6 (~1.2 km) for dense urban areas, precision 5 (~5 km) for suburban. The Quadtree dynamically adjusts: cells with >1000 businesses are split into finer grids. Ensures each cell has 50-500 results for efficient pagination.
Search Ranking Pipeline: Query results are ranked by: (1) distance, (2) average rating, (3) review count, (4) business category match, (5) personalization signal (past searches). Ranking computed in <10ms using a pre-scored index in Elasticsearch.
Review Ingestion: New reviews are written to PostgreSQL (source of truth) and async-indexed into Elasticsearch via Kafka. Review count and average rating are denormalized onto the business document in ES for fast retrieval. Eventual consistency window: <5 seconds.
Photo CDN: Business photos served from CDN with WebP format. Average business has 15 photos × 5 variants = 75 images. At 200M businesses: 15B image files. Use content-addressed storage with dedup. Lazy-load images on scroll — only load first 3 photos initially.
Autocomplete for Business Search: Business names indexed in a separate Trie (refreshed hourly from ES). Prefix queries return top-5 matching businesses with snippets. Sharded by city for locality. Handles 50K autocomplete queries/sec.
Cache Strategy: Cache popular search queries (e.g., 'pizza near me' in Manhattan) in Redis with 5-minute TTL. Cache hit ratio: 40%. For cache misses, query Elasticsearch and populate cache. Invalidate on new review or business update via Kafka event.
8. Runnable Production Starter Code
Scaffold