Geospatial Data
Redis geospatial commands (GEOADD, GEORADIUS, GEOSEARCH) store lat/lon as sorted-set members scored by geohash — Uber-style driver dispatch queries nearby entities in millisecon…
Introduction
Redis geospatial commands (GEOADD, GEORADIUS, GEOSEARCH) store lat/lon as sorted-set members scored by geohash — Uber-style driver dispatch queries nearby entities in milliseconds without scanning every pin in the city.
Under the hood GEO is a ZSET; GEORADIUS uses geohash indexing for radius search. Distances returned in m, km, mi, or ft with WITHDIST and WITHCOORD options.
App-side haversine over millions of points fails at scale — push location indexing to Redis or a dedicated geo service.
Understanding the topic
Key concepts
- GEOADD key lon lat member — add location (note lon before lat).
- GEORADIUS key lon lat radius unit — nearby search.
- GEOSEARCH — Redis 6.2+ unified search API.
- GEODIST — distance between two members.
- Stored as ZSET — scores are geohash-encoded.
- GEOPOS — retrieve coordinates of members.
flowchart LRMobile -->|lat lon| GEOADDGEOADD --> ZSet[Geohash ZSET]Dispatch -->|GEORADIUS| ZSetZSet -->|driver IDs| Matcher
Step-by-step explanation
- Lat/lon converted to 52-bit geohash score in ZSET.
- Radius search checks geohash cells overlapping circle.
- Results sorted by distance when requested.
- Updates via GEOADD replace member position.
- TTL applies to entire geo key like any key.
Syntax reference
Common commands
- Longitude first — common mistake is lat/lon swap.
- GEORADIUS COUNT 10 — limit results.
- Shard keys by city/region.
GEOADD drivers:nyc -73.985 40.758 driver:42GEORADIUS drivers:nyc -73.985 40.758 2 km WITHDIST WITHCOORDGEODIST drivers:nyc driver:42 driver:99 km
Informative example
Update driver location and find nearest five in Spring:
@Servicepublic class DriverGeo {private final StringRedisTemplate redis;public DriverGeo(StringRedisTemplate redis) {this.redis = redis;}public void updateLocation(String driverId, double lon, double lat) {redis.opsForGeo().add("drivers:sf", new Point(lon, lat), driverId);}public GeoResults<RedisGeoCommands.GeoLocation<String>> nearby(double lon, double lat) {return redis.opsForGeo().radius("drivers:sf",new Circle(new Point(lon, lat), new Distance(3, Metrics.KILOMETERS)),RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().limit(5));}}
Expire stale drivers with separate TTL key or periodic ZREM. Active pins only — not historical trail (use streams for that).
Real-world use
Real-world use cases
- Ride-share driver matching — GEORADIUS 3km.
- Store locator — nearest branches.
- Delivery fleet ETA input — distance sort.
- IoT asset tracking — last known position.
- Geofence entry — periodic radius check.
Best practices
- Partition by metro: drivers:{cityCode}.
- Remove inactive drivers — ZREM or key TTL.
- Limit GEORADIUS COUNT to cap work.
- Validate coordinates before GEOADD.
- Use read replicas for heavy read radius traffic.
- Combine with Kafka for location event pipeline.
Common mistakes
- Lat/lon argument order swapped.
- Single global key for all drivers worldwide.
- No cleanup of offline drivers — stale results.
- Storing full trip history in geo key.
Advanced interview questions
Q1BeginnerGEOADD argument order?
Q2BeginnerWhat structure backs Redis GEO?
Q3IntermediateGEORADIUS vs app-side loop?
Q4IntermediateScale geo to 1M active drivers?
Q5AdvancedDesign Uber dispatch cache?
Summary
GEO = ZSET + geohash for radius search.