from GEOADD to radius search in a store locator
Redis Geo-Commands do not store coordinates in a dedicated data structure but in a plain sorted set with a specially encoded score. Once you understand this mechanism, you can build radius searches, store locator features and distance calculations with a handful of commands, without a separate geo database.
Table of Contents
- 1. What Redis Geo-Commands really are
- 2. Geohashing internals: coordinates as a sorted set score
- 3. GEOADD in practice: adding locations
- 4. GEOSEARCH: the modern radius search
- 5. Store locator example step by step
- 6. GEODIST, GEOPOS and GEOHASH as tools
- 7. Performance and complexity of Geo-Commands
- 8. Limits and pitfalls of Geo-Commands
- 9. Geo-Commands compared to alternatives
- 10. Summary
- 11. FAQ
1. What Redis Geo-Commands really are
Redis Geo-Commands are not a dedicated data type but a thin command layer on top of the already existing sorted set structure. When a team first hears about GEOADD, the impression often forms that Redis introduces a new storage form for geo data internally. In reality, Redis uses the same skip-list based sorted set implementation for all Geo-Commands that also powers leaderboards or time series. The trick lies in how the score is encoded, not in a new data structure.
This decision by the Redis developers has a practical reason: anyone who already works with sorted sets already knows the complexity behavior, the persistence semantics and the replication logic of Geo-Commands, because it is exactly the same as for ZADD and ZRANGE. A GEOADD call is ultimately a ZADD call with a computed score. This transparency makes it possible to integrate Geo-Commands into existing Redis architectures without a separate learning curve.
In practice, Geo-Commands are used mainly for three tasks: radius searches such as store finders, distance calculations between two points and ranking locations by proximity. All three tasks can be solved with a handful of commands, without building a separate geo data system such as PostGIS. That is exactly what makes Redis Geo-Commands attractive for many use cases where speed matters more than centimeter-level geodetic precision.
2. Geohashing internals: coordinates as a sorted set score
The core of Redis Geo-Commands is a 52-bit interleaved geohash computed from longitude and latitude. Both coordinates are first normalized to 26 bits each and then interleaved bit by bit, so that a single integer value encodes both the horizontal and the vertical position. This 52-bit value is stored as the score in the sorted set, while the member is the name of the location, for example a store ID.
The reason for the interleaving is geometric: points that are spatially close to each other end up with numerically similar geohash values through the interleaving. That means an area on the map corresponds to a contiguous value range in the sorted set, with the well-known exception of edge cases at the boundaries of geohash cells. This is exactly the property that Redis Geo-Commands exploit to reduce radius searches to efficient range scans on the sorted set, instead of iterating over every point individually.
Important for understanding: the precision of the Redis geohash is about 0.6 meters at the finest level, which is more than enough for nearly all use cases. Since the score is a regular double value in the sorted set, all sorted set commands such as ZSCORE or ZRANGEBYSCORE can be applied directly to geo data, even though that is rarely necessary in practice because the dedicated Geo-Commands already provide the right abstraction.
# Connect to Redis and inspect the underlying data type
redis-cli> GEOADD stores:berlin 13.404954 52.520008 "store:mitte"
(integer) 1
# The geo index is a plain sorted set under the hood
redis-cli> TYPE stores:berlin
zset
# The raw score is the 52-bit interleaved geohash as a double
redis-cli> ZSCORE stores:berlin "store:mitte"
"3673983950505224"
# Standard sorted set commands still work on geo data
redis-cli> ZCARD stores:berlin
(integer) 1
3. GEOADD in practice: adding locations
The GEOADD command accepts a key plus any number of triples of longitude, latitude and member name. The order matters: Redis expects longitude first, then latitude, the opposite of the lat-lng notation common in many map services. This order mistake is one of the most frequent pitfalls when first using Redis Geo-Commands and leads to locations that appear shifted by hundreds of kilometers on the map.
Since Redis 6.2, GEOADD supports additional options such as NX, XX and CH, taken over from the regular ZADD options. With NX, only new members are added, existing locations remain unchanged. With XX, only existing members are updated. These options are especially valuable when a batch import should not accidentally overwrite existing store data while still adding new locations at the same time.
# Add multiple store locations in a single GEOADD call
redis-cli> GEOADD stores:berlin \
13.404954 52.520008 "store:mitte" \
13.331249 52.506342 "store:tiergarten" \
13.454972 52.487537 "store:kreuzberg"
(integer) 3
# NX: only add new members, never overwrite existing coordinates
redis-cli> GEOADD stores:berlin NX 13.404954 52.520008 "store:mitte"
(integer) 0
# XX: only update coordinates of members that already exist
redis-cli> GEOADD stores:berlin XX 13.405100 52.520200 "store:mitte"
(integer) 0
4. GEOSEARCH: the modern radius search
GEOSEARCH has been the recommended command for radius searches since Redis 6.2 and replaces the older, now deprecated commands GEORADIUS and GEORADIUSBYMEMBER. The decisive advantage of GEOSEARCH lies in the flexibility of the search shape: instead of only supporting circular radii, GEOSEARCH also allows rectangular search boxes via the BYBOX option, which is often the more natural search shape for map-based applications with a visible viewport.
The search center can be specified either as an explicit coordinate via FROMLONLAT or as a reference to an already stored member via FROMMEMBER. The latter is especially handy for queries such as "show me all stores near store X", without first having to look up the coordinates of the reference store separately. Results can be sorted by distance with ASC or DESC and limited to a maximum count via COUNT, which is essential for paginated result lists.
# Radius search: find stores within 5 km of a coordinate, sorted by distance
redis-cli> GEOSEARCH stores:berlin FROMLONLAT 13.4050 52.5200 \
BYRADIUS 5 km ASC WITHCOORD WITHDIST COUNT 10
1) 1) "store:mitte"
2) "0.0134"
3) 1) "13.40495389699935150"
2) "52.52000850996238098"
2) 1) "store:tiergarten"
2) "4.8213"
3) 1) "13.33124905824661255"
2) "52.50634169416604663"
# Box search: rectangular viewport instead of a circular radius
redis-cli> GEOSEARCH stores:berlin FROMLONLAT 13.4050 52.5200 \
BYBOX 10 10 km ASC WITHDIST
# Search relative to an existing member instead of raw coordinates
redis-cli> GEOSEARCH stores:berlin FROMMEMBER "store:mitte" \
BYRADIUS 3 km ASC
5. Store locator example step by step
A classic use case for Redis Geo-Commands is the store locator of an online shop that also runs physical stores alongside pure e-commerce. The flow is always the same: during the import of store data, all locations are written once via GEOADD into a sorted set, usually as part of a nightly batch job or directly when a new store is created in the backend. The user query "stores near me" then delivers browser coordinates that are passed directly to GEOSEARCH.
The practical charm lies in the response time: since the radius search works on the geohash range already indexed in the sorted set, latency stays in the low millisecond range even with tens of thousands of stores. For a mid-sized retail chain with a few hundred to a few thousand locations, a dedicated geo database is often overkill when Redis is already used as a cache layer anyway. Geo-Commands can then be integrated directly without any extra infrastructure.
For display on a map, GEOSEARCH is typically combined with WITHCOORD to return the coordinates directly, and WITHDIST to show the distance to the user. In addition, detail data such as opening hours and address are usually stored in a separate hash per store ID, so the sorted set stays lean and is exclusively responsible for the geographic search. This separation of geo index and detail data is a proven pattern for production store locator systems.
6. GEODIST, GEOPOS and GEOHASH as tools
Besides GEOADD and GEOSEARCH, Redis Geo-Commands offer three additional commands for common detail tasks. GEODIST calculates the distance between two already stored members and accepts a unit as an optional parameter, m, km, mi or ft. This is useful when two stores need to be compared directly, without going through a full radius search.
GEOPOS returns the original coordinates of one or more members, internally decoding the geohash score back into longitude and latitude. Because the encoding introduces a small amount of rounding, the returned coordinates deviate slightly from the originally entered values, typically within a range of a few centimeters. GEOHASH finally returns the standard geohash string in the 11-character format also used by external services such as geohash.org, which simplifies data exchange with other systems.
# Distance between two members, in kilometers
redis-cli> GEODIST stores:berlin "store:mitte" "store:kreuzberg" km
"6.2871"
# Decode stored geohash scores back into coordinates
redis-cli> GEOPOS stores:berlin "store:mitte" "store:kreuzberg"
1) 1) "13.40495389699935150"
2) "52.52000850996238098"
2) 1) "13.45497101545333862"
2) "52.48753627091235755"
# Standard 11-character geohash string for interop with external systems
redis-cli> GEOHASH stores:berlin "store:mitte"
1) "u33dc1v0j00"
7. Performance and complexity of Geo-Commands
The complexity of GEOADD matches that of ZADD: O(log N) per added element, since the underlying skip list finds the insertion position in logarithmic time. GEOSEARCH is somewhat more involved, because Redis internally computes several geohash cells that cover the requested radius or box, and then performs a range scan on the sorted set for each cell. The overall complexity is O(N plus log(M)), where N is the number of returned elements and M is the total number of members in the set.
In practice this complexity stays irrelevant for typical store locator sizes, since even with several tens of thousands of locations the number of hits actually within the search radius usually remains in the double digits. It only becomes critical for very large-area searches with high point density, for example searching for all vehicles of a large fleet within a metropolitan area. For such cases it is advisable to consistently set the COUNT parameter to keep the result set, and therefore the response time, predictably bounded.
8. Limits and pitfalls of Geo-Commands
Redis Geo-Commands are not a replacement for a full-fledged GIS system. There is no support for polygons, no route calculation and no consideration of road networks, all distances are pure straight-line distances based on the haversine formula. Anyone who needs real travel times or complex geographic shapes such as delivery areas must resort to specialized systems such as PostGIS or external routing APIs.
Another pitfall concerns the value range: Redis only accepts coordinates within minus 85.05112878 to 85.05112878 degrees of latitude, a limitation that stems from the Mercator projection on which the internal geohashing is based. Locations near the poles cannot be represented this way, which is irrelevant for the vast majority of use cases in practice, however. There is also no native delete command for individual geo members, instead ZREM is used on the same key, since it is technically a sorted set.
9. Geo-Commands compared to alternatives
The decision between Redis Geo-Commands and a dedicated geo database depends on the requirement profile. The following comparison shows the key differences for typical decision situations.
| Requirement | Redis Geo-Commands | PostGIS / dedicated GIS | Recommendation |
|---|---|---|---|
| Radius search, low millisecond latency | Very fast, in memory | Fast, but disk I/O possible | Redis Geo-Commands |
| Polygons, delivery areas | Not supported | Full GIS functionality | PostGIS |
| Route calculation / travel time | Straight line only | Possible with routing extension | External routing API |
| Additional infrastructure | None, if Redis is already running | Separate database needed | Redis Geo-Commands |
| Millions of locations, complex queries | Limited scaling per set | Built for large data volumes | PostGIS |
For most store locator and proximity use cases in e-commerce, Redis Geo-Commands are fully sufficient and save the operation of an additional system. Only when polygons, route planning or very large volumes with complex spatial queries are required does the switch to a dedicated GIS pay off.
10. Summary
Redis Geo-Commands build on the proven sorted set structure and encode coordinates as a 52-bit interleaved geohash in the score. GEOADD adds locations, GEOSEARCH has replaced the old GEORADIUS commands since Redis 6.2 and offers both circular and rectangular searches. GEODIST, GEOPOS and GEOHASH complement the core functionality for distance calculation, coordinate lookup and interop with external systems.
For store locator applications, proximity searches and simple spatial rankings, Geo-Commands are a lightweight, extremely fast solution without additional infrastructure. Anyone who needs polygons, route calculation or centimeter-level geodetic precision should reach for a dedicated GIS system such as PostGIS. The right choice depends on the actual requirement, not on the mere availability of Geo-Commands in Redis.
Redis Geo-Commands, the essentials at a glance
Data structure
Geo-Commands use a plain sorted set, the score is a 52-bit interleaved geohash from longitude and latitude.
Core commands
GEOADD to add, GEOSEARCH for radius and box searches, GEODIST for distances, GEOPOS and GEOHASH for coordinates.
Watch the order
Redis expects longitude before latitude, the opposite of the usual lat-lng notation used by many map services.
Limits
No polygons, no route calculation, straight line only. Use PostGIS or an external API for full GIS functionality.