Redis 6.2 introduced the GEOSEARCH and GEOSEARCHSTORE commands and deprecated the older GEORADIUS family. What do the newer commands add, and why could the original GEORADIUS not be executed on a read-only replica?
answer
- GEOSEARCH = FROMLONLAT|FROMMEMBER × BYRADIUS|BYBOX
- BYBOX = map viewport
- GEORADIUS had STORE → flagged write → replicas rejected it
- _RO variants exist only for that reason
- GEOSEARCHSTORE + STOREDIST = scores become distances
basics
~20 sGEOSEARCH unifies GEORADIUS and GEORADIUSBYMEMBER (FROMLONLAT or FROMMEMBER) and adds rectangle search with BYBOX. GEORADIUS had an optional STORE clause, so Redis classified it as a write command and replicas refused it — hence the separate GEORADIUS_RO. GEOSEARCH is read-only; GEOSEARCHSTORE is the writing variant.
solid answer
~50 sThree things changed. **Unification**: `GEORADIUS` (search around a point) and `GEORADIUSBYMEMBER` (search around a stored member) collapse into one `GEOSEARCH` with `FROMLONLAT lon lat` or `FROMMEMBER member`. **A new shape**: `BYBOX width height unit` searches an axis-aligned rectangle alongside the existing `BYRADIUS`. A box is the natural query for a map viewport, where a circle either over- or under-covers the visible area. **A clean read/write split**: `GEORADIUS` carried an optional `STORE`/`STOREDIST` clause that wrote results into a destination key. Because the command *could* write, Redis flagged the whole command as a write command — so replicas rejected it, and it had to be replicated and journalled even for pure reads. That is why `GEORADIUS_RO`/`GEORADIUSBYMEMBER_RO` existed. In 6.2 the concern is split: `GEOSEARCH` is purely read-only and runs on replicas, while `GEOSEARCHSTORE dst src ... [STOREDIST]` is the explicit write command. The old commands still work but are deprecated; new code should use `GEOSEARCH`.
code
text · 10 lines# pre-6.2
GEORADIUS drivers -122.41 37.77 5 km ASC COUNT 10
GEORADIUSBYMEMBER drivers driver:17 2 km ASC
GEORADIUS drivers -122.41 37.77 5 km STORE nearby # write command
# 6.2+
GEOSEARCH drivers FROMLONLAT -122.41 37.77 BYRADIUS 5 km ASC COUNT 10
GEOSEARCH drivers FROMMEMBER driver:17 BYRADIUS 2 km ASC
GEOSEARCH drivers FROMLONLAT -122.41 37.77 BYBOX 4 3 km ASC # map viewport
GEOSEARCHSTORE nearby drivers FROMLONLAT -122.41 37.77 BYRADIUS 5 km ASC STOREDISTgo deeper
Know that GEOSEARCH is the modern query command and that it supports both a point and a member as the centre, and both a radius and a box.
Explain the read/write split: GEORADIUS's optional STORE flagged it as a write, hence the _RO variants; GEOSEARCH is read-only and GEOSEARCHSTORE is the writer.
Add the operational payoff — proximity reads can be served from replicas — plus cluster slot co-location for GEOSEARCHSTORE and the ANY short-circuit tradeoff.
Use it as an example of command-table flags being static per command: capability, replication behaviour and replica routing all follow the flag, so mixing a write option into a read command has architectural cost.
## The old surface Before Redis 6.2 the geo query API was four commands: - `GEORADIUS key lon lat radius unit ...` — circle around an explicit point. - `GEORADIUSBYMEMBER key member radius unit ...` — circle around an already-indexed member. - `GEORADIUS_RO`, `GEORADIUSBYMEMBER_RO` — read-only twins of the two above. The `_RO` twins existed because of one design mistake: `GEORADIUS` accepted an optional `STORE destkey` (write the matching members into a new sorted set, keeping their geohash scores) and `STOREDIST destkey` (write them scored by distance from the centre instead). Redis classifies a command as read or write **statically, by command table flag**, not per invocation. A command that can write is a write command always. That had three consequences: 1. **Replicas refused it.** A replica is read-only by default; issuing `GEORADIUS` against one returned an error even when you had passed no `STORE` clause. Scaling reads by pointing clients at replicas broke unless you remembered to call `GEORADIUS_RO`. 2. **Replication and AOF overhead.** Being a write command, it participated in the write path bookkeeping instead of being treated as a plain lookup. 3. **Cluster friction.** `STORE` writes a second key, which must live in the same hash slot as the source key — so the destination name has to share a hash tag with the source, or the command fails with a cross-slot error. ## What 6.2 introduced `GEOSEARCH` replaces both query commands with orthogonal arguments: ``` GEOSEARCH key <FROMMEMBER m | FROMLONLAT lon lat> <BYRADIUS r unit | BYBOX w h unit> [ASC|DESC] [COUNT n [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH] ``` - **Centre** and **shape** are now independent choices instead of being baked into the command name. That is why four commands became one. - **`BYBOX`** is genuinely new capability: an axis-aligned rectangle of the given width and height centred on the search point. Map UIs pan and zoom rectangles, so a box matches the viewport exactly; approximating it with a circle either fetches points the user cannot see or misses points in the corners. - **`COUNT n ANY`** (also 6.2) short-circuits: return as soon as `n` matches are found instead of collecting and ranking all of them. Much cheaper in dense areas, but the `n` you get are arbitrary, not the nearest. `GEOSEARCHSTORE dst src <FROM…> <BY…> [ASC|DESC] [COUNT n [ANY]] [STOREDIST]` is the separate write command. By default the destination sorted set keeps the members' geohash scores, so the result is itself a valid geo index you can query again. With `STOREDIST` the scores become the distance from the centre in the requested unit — handy for paging by proximity with `ZRANGE`, but the destination is then no longer a usable geo index. ## Why the split matters operationally With `GEOSEARCH` flagged read-only: - It runs on replicas, so proximity queries can be scaled horizontally by adding replicas rather than by resizing the primary. - It is safe on a primary that has hit a write-blocking condition (for example when a persistence error has put the server into a state that refuses writes). - Intent is visible in the command name: a code reviewer or an audit of the command stream can tell reads from writes without parsing arguments. And `GEOSEARCHSTORE`, being explicitly a write, is where you consciously think about slot co-location in a cluster (`{city}` hash tag on both source and destination), destination TTL, and the memory cost of materialising a result set. ## Migration guidance The old commands are deprecated as of 6.2 but still function — Redis does not remove commands lightly, and client libraries still expose them. Mechanical mapping: - `GEORADIUS k lon lat r unit` → `GEOSEARCH k FROMLONLAT lon lat BYRADIUS r unit` - `GEORADIUSBYMEMBER k m r unit` → `GEOSEARCH k FROMMEMBER m BYRADIUS r unit` - `GEORADIUS … STORE d` → `GEOSEARCHSTORE d k …` - `GEORADIUS_RO …` → `GEOSEARCH …` (the `_RO` variants become pointless) One caveat when targeting mixed fleets: if any server in the deployment predates 6.2, `GEOSEARCH` is simply an unknown command there, so a rollout has to upgrade servers before clients switch.
- What is the difference between the destination key written by GEOSEARCHSTORE with and without STOREDIST?Without `STOREDIST`, the destination sorted set keeps each member's 52-bit geohash score, so the result is itself a valid geo index that further `GEO*` commands can query. With `STOREDIST`, the scores are replaced by the distance from the search centre in the requested unit — convenient for paging by proximity with `ZRANGE`, but the destination is no longer decodable as coordinates.
- When would you choose BYBOX over BYRADIUS?Whenever the query area is genuinely rectangular — most obviously a map viewport that the user has panned or zoomed. A circle inscribed in that rectangle misses the corners, and a circumscribed circle returns points outside the visible area that the client must then discard. A box matches the region exactly and avoids the wasted transfer.
saying these in an interview costs you the question
- Believing GEORADIUS was rejected on replicas because reads were unsafe, rather than because the optional STORE clause made it a write command
- Thinking GEOSEARCH removed radius search — it kept BYRADIUS and added BYBOX
- Assuming the deprecated GEORADIUS commands were removed and old code stopped working
- Using GEOSEARCHSTORE in a cluster without a shared hash tag between source and destination
- Treating COUNT n ANY as 'the nearest n' — it returns whichever n it finds first