Redis Commands: Geography Edition
- Commands (with examples)
- How to Add Geo Commands to Redis
- Origin Story
- How it Works
- Bonus: Real Time Streaming Location Updates
- Benchmarking
Note (version): I wrote this originally as a Redis module in early 2014, then Redis included a modified version directly into official Redis releases in 2016.
Note (implementation): The implementation included with Redis 3.2+ changed some parameter orderings from my original (intentional, well-thought-out) design and also removed some features listed on this page. YMMV.
Commands
geoadd geoset latitude longitude member
geoadd
requires a minimum of four parameters: your geo set (spoiler: it’s just a zset), the lat/long you’re adding, and the member this lat/long belongs to. geoadd
also accepts unlimited number of adds per run with repeating latitude longitude member.
Note: an API similar to this was added to Redis in mid-2015. The geo included in Redis broke this interface and rearranged parameters in a nonsensical order. Consult actual Redis documentation if you use built-in Redis geo.
Single add
Return value: 1 if member is new or 0 if member is updated.
Multi add
Return value: count of members submitted.
127.0.0.1:6379> geoadd nyc 40.7648057 -73.9733487 "central park n/q/r" 40.7362513 -73.9903085 "union square" 40.7126674 -74.0131604 "wtc one" 40.6428986 -73.7858139 "jfk" 40.7498929 -73.9375699 "q4" 40.7480973 -73.9564142 4545
(integer) 6
Browse underlying zset
127.0.0.1:6379> zrange nyc 0 -1
1) "wtc one"
2) "union square"
3) "central park n/q/r"
4) "4545"
5) "lic market"
6) "q4"
7) "jfk"
georadius geoset latitude longitude radius units [withdistance] [withcoordinates] [withhash] [withgeojson] [withgeojsoncollection] [noproperties] [asc|desc]
georadius
takes a minimum of five arguments and a maximum of twelve arguments. The radius argument is the search radius in units. Valid units are: m, km, ft, mi. You can return the distance from your latitude longitude by adding withdistance. The returned distance is in units of units.
Simple search
Return value: array of members within radius units from your requested latitude longitude.
127.0.0.1:6379> georadius nyc 40.7598464 -73.9798091 3 km
1) "central park n/q/r"
2) "union square"
3) "4545"
Search with distance returned
Return value: nested multi-bulk reply with each member in a different top-level multi-bulk reply.
127.0.0.1:6379> georadius nyc 40.7598464 -73.9798091 3 km withdistance
1) 1) "central park n/q/r"
2) "0.78"
2) 1) "union square"
2) "2.77"
3) 1) "4545"
2) "2.37"
Distance search with sorting
Return value: same as above, but now sorted. asc (or ascending) returns the closest to latitude longitude as the first entry, with distance increasing as you go down the result list. desc (or descending) returns the most distance from latitude longitude as the first entry, with distance decreasing for subsequent entries.
127.0.0.1:6379> georadius nyc 40.7598464 -73.9798091 3 km withdistance descending
1) 1) "union square"
2) "2.77"
2) 1) "4545"
2) "2.37"
3) 1) "central park n/q/r"
2) "0.78"
georadiusbymember geoset member radius units [withdistance] [withcoordinates] [withhash] [withgeojson] [withgeojsoncollection] [noproperties] [asc|desc]
georadiusbymember
is exactly the same as georadius
except your search is centered at coordinates for member in geoset.
Search radius by existing member
127.0.0.1:6379> georadiusbymember nyc "wtc one" 7 km
1) "wtc one"
2) "union square"
3) "central park n/q/r"
4) "4545"
5) "lic market"
Search around existing member with distances returned
127.0.0.1:6379> georadiusbymember nyc "wtc one" 7 km withdist
1) 1) "wtc one"
2) "0.00"
2) 1) "union square"
2) "3.25"
3) 1) "central park n/q/r"
2) "6.70"
4) 1) "4545"
2) "6.20"
5) 1) "lic market"
2) "6.90"
Search with results in descending distance
127.0.0.1:6379> georadiusbymember nyc "wtc one" 7 km withdistance desc
1) 1) "central park n/q/r"
2) "6.70"
2) 1) "4545"
2) "6.20"
3) 1) "lic market"
2) "6.90"
4) 1) "union square"
2) "3.25"
5) 1) "wtc one"
2) "0.00"
GeoJSON results
Return value: nested multi-bulk reply with each returned member having a GeoJSON feature returned too. Note: the distance, units, member name, and set name and all returned in the GeoJSON too. You may exclude properties in GeoJSON by adding noproperties to your arguments.
127.0.0.1:6379> georadiusbymember nyc "wtc one" 7 km withgeojson
1) 1) "wtc one"
2) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-74.01316255331,40.712667181451]},\"properties\":{\"distance\":0,\"member\":\"wtc one\",\"units\":\"km\",\"set\":\"nyc\"}}"
2) 1) "union square"
2) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.990310132504,40.736250227118]},\"properties\":{\"distance\":3.2543954573357,\"member\":\"union square\",\"units\":\"km\",\"set\":\"nyc\"}}"
3) 1) "central park n/q/r"
2) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.973347842693,40.764806395699]},\"properties\":{\"distance\":6.7000029092799,\"member\":\"central park n\\/q\\/r\",\"units\":\"km\",\"set\":\"nyc\"}}"
4) 1) "4545"
2) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.956412374973,40.748097513816]},\"properties\":{\"distance\":6.1975173818005,\"member\":\"4545\",\"units\":\"km\",\"set\":\"nyc\"}}"
5) 1) "lic market"
2) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.945495784283,40.747532270998]},\"properties\":{\"distance\":6.8968709532079,\"member\":\"lic market\",\"units\":\"km\",\"set\":\"nyc\"}}"
GeoJSON collection result
Return value: array of matching members with one entry for each result plus one final result for an aggregate GeoJSON FeatureCollection1. The FeatureCollection contains all radius matching points in one GeoJSON document. You can remove properties from the GeoJSON by adding noproperties to your arguments.
127.0.0.1:6379> georadiusbymember nyc "wtc one" 7 km withgeojsoncollection
1) "wtc one"
2) "union square"
3) "central park n/q/r"
4) "4545"
5) "lic market"
6) "{\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-74.01316255331,40.712667181451]},\"properties\":{\"distance\":0,\"member\":\"wtc one\",\"units\":\"km\",\"set\":\"nyc\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.990310132504,40.736250227118]},\"properties\":{\"distance\":3.2543954573357,\"member\":\"union square\",\"units\":\"km\",\"set\":\"nyc\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.973347842693,40.764806395699]},\"properties\":{\"distance\":6.7000029092799,\"member\":\"central park n\\/q\\/r\",\"units\":\"km\",\"set\":\"nyc\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.956412374973,40.748097513816]},\"properties\":{\"distance\":6.1975173818005,\"member\":\"4545\",\"units\":\"km\",\"set\":\"nyc\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-73.945495784283,40.747532270998]},\"properties\":{\"distance\":6.8968709532079,\"member\":\"lic market\",\"units\":\"km\",\"set\":\"nyc\"}}],\"type\":\"FeatureCollection\"}"
geoencode latitude longitude [radius units] [withgeojson]
Encode latitude and longitude to highest geohash accuracy
Return value: nested multi-bulk reply with 1: the 52-bit geohash integer for your latitude longitude, 2: The minimum corner of your geohash, 3: The maximum corner of your geohash, 4: The averaged center of your geohash.
127.0.0.1:6379> geoencode 41.2358883 1.8063239
1) (integer) 3471579339700058
2) 1) "41.235888125243704"
2) "1.8063229322433472"
3) 1) "41.235890659964866"
2) "1.806328296661377"
4) 1) "41.235889392604285"
2) "1.8063256144523621"
Encode plus obtain result as GeoJSON
Return value: same as above, but with 5: GeoJSON of your encoded geohash center, 6: GeoJSON bounding box showing the extent of this geohash area. By default, we encode to the highest 52-bit geohash accuracy, which will always give us a bounding box containing a 2 ft (0.6 m) radius.
127.0.0.1:6379> geoencode 41.2358883 1.8063239 withgeojson
1) (integer) 3471579339700058
2) 1) "41.235888125243704"
2) "1.8063229322433472"
3) 1) "41.235890659964866"
2) "1.806328296661377"
4) 1) "41.235889392604285"
2) "1.8063256144523621"
5) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.8063256144524,41.235889392604]},\"properties\":{}}"
6) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[1.8063229322433,41.235888125244],[1.8063282966614,41.235888125244],[1.8063282966614,41.235890659965],[1.8063229322433,41.235890659965],[1.8063229322433,41.235888125244]]]},\"properties\":{}}"
Encode geohash covering radius units area plus obtain result as GeoJSON
Return value: same as the GeoJSON encode above, except it encodes your coordinates to a geohash with bounding box radius units. You can use your GeoJSON Polygon to easily see the bounding box of this large geohash. Note: geohash bounding boxes are not centered at your requested coordinate. See How it Works for more details.
127.0.0.1:6379> geoencode 41.2358883 1.8063239 20 km withgeojson
1) (integer) 3471579115683840
2) 1) "41.1966405028125"
2) "1.7578125"
3) 1) "41.362755988710937"
2) "2.109375"
4) 1) "41.279698245761722"
2) "1.93359375"
5) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.93359375,41.279698245762]},\"properties\":{}}"
6) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[1.7578125,41.196640502812],[2.109375,41.196640502812],[2.109375,41.362755988711],[1.7578125,41.362755988711],[1.7578125,41.196640502812]]]},\"properties\":{}}"
geodecode geohash [withgeojson]
Decode geohash
Return value: nested multi-bulk with 1: minimum decoded corner, 2: maximum decoded corner, 3: averaged center of bounding box.
127.0.0.1:6379> geodecode 3471579339700058
1) 1) "41.235888125243704"
2) "1.8063229322433472"
2) 1) "41.235890659964866"
2) "1.806328296661377"
3) 1) "41.235889392604285"
2) "1.8063256144523621"
Decode geohash and obtain GeoJSON result
Return value: same as above, but with 4: GeoJSON Point of your geohash center and 5: bounding box for this geohash. Note: we always decode to highest accuracy, so your decode GeoJSON Polygon bounding box will always contain a 2 ft (0.6 m) radius;
127.0.0.1:6379> geodecode 3471579339700058 withgeojson
1) 1) "41.235888125243704"
2) "1.8063229322433472"
2) 1) "41.235890659964866"
2) "1.806328296661377"
3) 1) "41.235889392604285"
2) "1.8063256144523621"
4) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.8063256144524,41.235889392604]},\"properties\":{}}"
5) "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[1.8063229322433,41.235888125244],[1.8063282966614,41.235888125244],[1.8063282966614,41.235890659965],[1.8063229322433,41.235890659965],[1.8063229322433,41.235888125244]]]},\"properties\":{}}"
Other Operations
Removing members
A geoset is actually just a Redis zset. To remove a member from your geoset, use the ZREM
command: ZREM _geoset_ _member_
.
Counting members
Since a geoset is just a zset, you can use ZCARD _geoset_
to count your set size.
Mult-set operations
You can also use ZUNIONSTORE
and ZINTERSTORE
on your geoset, but be careful to avoid the default behavior of summing scores (adding two geohash scores together results in meaningless output).
How to Add Geo Commands to Redis
Geo commands are implemented as modular Redis commands for Dynamic Redis.
Dynamic Redis is a simple set of patches on top of regular Redis allowing new commands to load using shared libraries (no need to patch and recompile Redis to add new commands!).
The geo module is distributed as part of Redis Module ToolKit.
Cloning, compiling, and loading geo.so
Below is a set of commands to:
- clone the current development branch of Dynamic Redis
- the current Redis Module ToolKit
- compile the
geo.so
module - and load
geo.so
into a Dynamic Redis server running on the default Redis port
mkdir -p ~/repos
cd ~/repos
git clone https://github.com/mattsta/redis
cd redis
git checkout dynamic-redis-unstable
cd ..
git clone https://github.com/mattsta/krmt
cd krmt
make -j
redis-cli config set module-add `pwd`/geo.so
If you would like to run a Dynamic Redis based on a released Redis version, use a stable branch listed at Dynamic Redis.
Quick Validation Test
You can check if geo.so
loaded successfully by one or more of:
redis-cli geoencode 0 0
redis-cli INFO modules
- or just watch your
redis-server
terminal (or log) for successful module addition output
Note: If INFO modules
fails, you are not running a Dynamic Redis. You need to run a Dynamic Redis to load modules.
Origin Story
Location is important. Everything exists somewhere. Knowing where things are is more and more important in the world. Every mobile phone is a complex location tracking device these days, but how are we using all the data we have access to now?
Location information presents a very basic problem. Location information is inherently four dimesional: (latitude, longitude, elevation) at a given time. For ease of data processing, we tend to ignore elevation and time when presenting location data and only focus on latitude/longitude grid coordinates.
Things exist
Things exist at a location (a latitude and a longitude). We ignore elevation for now. Discovering if one (latitude, longitude) pair is near another requires a two dimensional index. Two dimensional indexing is largely a solved problem, but not as widely implemented as very simple one-dimensional indexing using range searches.
What we need is a way to represent a two dimensional space in only one dimension. For integers, we can use simple space filling curves. We can represent a pair of integer (x,y) coordinates in only one integer, but geographic data isn’t only integers.
In 2008, a public domain implementation for encoding latitude/longitude pairs down to one dimension showed up as geohash. Now we can easily encode our geographic information (minus elevation and time) into one range-searchable entity.
The Geohash
A Geohash is traditionally represented as a base-32 encoded string so locations can be easily shared with a short, easy to type string. Most databases have a way of running range searches over strings, so you can get a rough ability to search based on location, but because it uses a string encoding, each character represents between 1 and 5 bits. It’s not an efficient or accurate encoding for doing range searches.
The Improved Geohash
Nothing about using a Geohash requires you encode it as an ASCII string. And, actually, using the Geohash in raw binary form is much more accurate and precise than a base-32 conversion.
The accuracy of a Geohash depends on its length. A 32-bit Geohash represents an area with an average error of about 600 meters. A 64-bit geohash represents an area with an average error of 18 centimeters. A 128-bit geohash represents an area with an average error of the width of a strand of DNA.
Obviously a 128-bit Geohash is overkill. We don’t need to target DNA with femtodrones (yet). But, going up the scale, a 32-bit Geohash isn’t nearly accurage enough. A 42-bit Geohash gives us an accuracy error of 20 meters. That’s better, but still not great—there would be too much ancillary damage when targeting a person in a 20 meter radius.
A happy a compromise is a 52-bit Geohash. 52-bits gives us accuracy down to 0.6 m (2 ft), which, unless you’re on crowded public transport or at a concert, is about the amount of space a human-sized person takes up in the world.
How it Works
Geohash Implementation
Ardb, an extended Redis clone written in C++, recently released their integer-only Geohash implementation used in their 2D Spatial Index.
Upon seeing the brilliant non-text-Geohash, I just had to port Geo Indexing back to Redis. (Cross polination of open source projects is great, isn’t it?)
So, I forked their Geohash implementation, bikesheded the heck out of it (formatting fixes, coding convention fixes, friendiler interfaces, some logic improvements) and built Redis Geo Commands around my heavily modified 52-bit Geohash implementation originally provided by Ardb.
Why 52-bits?
Redis stores all scores of sorted set (zset) as type double
. Doubles are 64-bit floating point representations—but—they have a special property: 52-bit integers are perfectly represented with no loss of accuracy (as seen in double precision floats have direct representations for 52-bit integers).
By using 52-bit integers for our Geohash, we can attach a two dimensional location to every zset member.
The Geohash encoding guarantees, within known bounds, a range search between a lower range Geohash and a higher range Geohash will include all coordinates between. We get free two dimensional indexing with a simple range search. Awesome.
(Yes, we also cover the 8 + 1 (neighbors + center) search scenarios too.)
Investigate Geohash Storage in Redis
When using georadius
or georadiusbymember
, you can request the underlying Geohash too by adding withhash
to your command.
You can use zset commands directly to investigate the structure of your geoset too:
127.0.0.1:6379> zrange nyc 0 -1 withscores
1) "wtc one"
2) "1791873972053020"
3) "union square"
4) "1791875485187452"
5) "central park n/q/r"
6) "1791875761332224"
7) "4545"
8) "1791875796750882"
9) "lic market"
10) "1791875804419201"
11) "q4"
12) "1791875830079666"
13) "jfk"
14) "1791895905559723"
You could even implement GEOADD
yourself by encoding your coordinates with GEOENCODE
then using the returned Geohash as the score in a ZADD _zset_ _score_ _member_
, but GEOADD
provides a nicer interface, and GEOADD
enables the special use case of our next section.
Bonus: Real Time Streaming Location Updates
Big data real time streaming microservices. (Billion dollars nao, plz.)
Everything is real time. Imagine you have users pushing high frequency location updates. Maybe they are on a train and updating their location once a second. How do you show their location updates in a live view?
Thanks to the magic of Geo Commands combined with Redis PubSub, you can get notified of every location update by subscribing to a geo-themed Redis PubSub channel.
Redis Geo PubSub Channel
All values set by GEOADD
generate a Redis PUBLISH
event on channel __geo:[geoset]:[member]
.
Track ALL THE THINGS
Subscribe to all location updates: PSUBSCRIBE __geo:*
Subscribe to location updates for a specific geoset: PSUBSCRIBE __geo:[geoset]:*
Subscribe to location updates for a specific (geoset, member): SUBSCRIBE __geo:[geoset]:[member]
(Usage note: PSUBSCRIBE
is “pattern subscribe” and matches anything after *
— SUBSCRIBE
matches only the exact channel you specify.)
Trivial Use Case
You can imagine a scenario where one user is tracking another in real-time by SUBSCRIBE __geo:members:5331
to track user 5531 in the members geoset.
Bulk PubSub Location Tracking Example
Let’s do a bulk GEOADD
while also being subscribed to all geoset PubSub channels in another terminal.
Update locations in Terminal 1:
127.0.0.1:6379> geoadd nyc 40.7648057 -73.9733487 "central park n/q/r" 40.7362513 -73.9903085 "union square" 40.7126674 -74.0131604 "wtc one" 40.6428986 -73.7858139 "jfk" 40.7498929 -73.9375699 "q4" 40.7480973 -73.9564142 4545
(integer) 6
Real-time updates received live in Terminal 2:
127.0.0.1:6379> psubscribe __geo:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__geo:*"
3) (integer) 1
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:central park n/q/r"
4) "40.7648057 -73.9733487"
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:union square"
4) "40.7362513 -73.9903085"
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:wtc one"
4) "40.7126674 -74.0131604"
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:jfk"
4) "40.6428986 -73.7858139"
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:q4"
4) "40.7498929 -73.9375699"
1) "pmessage"
2) "__geo:*"
3) "__geo:nyc:4545"
4) "40.7480973 -73.9564142"
(Note: Terminal 2 is configured to psubscribe __geo:*
before the GEOADD
ran. Redis PubSub only sends live results. If you are not subscribed when an event happens, the event cannot be sent to you.)
Benchmarking
Raw Geohashing
A simple speed test is distributed with the geo commands: geo-test
:
% ./geo-test
Sanity check: first latlong: (40.712667, 40.712667)
Running an encode speed test...
Elapsed encode time: 8.556810 seconds
Against 40000000 total encodes
Speed: 4674639.263931 encodes per second
(That's 213.920250 nanoseconds per encode)
Now running a decode speed test...
Elapsed decode time: 7.233123 seconds
Against 40000000 total decodes
Speed: 5530114.723612 decodes per second
(That's 180.828075 nanoseconds per decode)
Encode throughput: 4.6 million encodes per second. Decode throughput: 5.5 million decodes per second.
These numbers are for translating latitude/longitude to and from 52-bit Geohashes. Redis has network overhead, internal data structure overhead, and other “server processing” features, so we don’t get 5.5 million operations per second, but we still get enough.
Redis Geo Commands
Our performance is pretty good. There is room for improvement in a few areas, but geo commands are perfectly usable for high performance applications today.
This is a series of benchmarks as of the first public geo commands release (0.3).
All benchmarks were run on my 2013 i7 OS X laptop (which is typically faster than my 2008 i7 server). There’s also a good chance these commands were run with redis-server
being simultaneously executed by a debugger and introspected by a memory leak checker. Your performance will be better on a non-laptop.
You can easily increase your performance by running multiple redis-server
processes on one multi-core server (or on multiple servers). If your server has 8 cores, you can fully utilize it by running 6 to 10 redis-server
processes (depending on your workload).
Bulk Benchmark Sample Pipeline of 32 Requests
redis-benchmark -P 32 -n 100000 geoencode 0 0
179533.22 requests per second
187617.27 requests per second
188679.25 requests per second
191204.59 requests per second
191570.88 requests per second
192307.70 requests per second
192678.23 requests per second
193423.59 requests per second
193798.45 requests per second
195694.72 requests per second
redis-benchmark -P 32 -n 100000 geoencode 0 0 withjson
73099.41 requests per second
73313.78 requests per second
73475.39 requests per second
73475.39 requests per second
73909.83 requests per second
75018.76 requests per second
75187.97 requests per second
75187.97 requests per second
75815.01 requests per second
76804.91 requests per second
redis-benchmark -P 32 -n 100000 geodecode 3471579339700058
160256.41 requests per second
163132.14 requests per second
170940.17 requests per second
173611.12 requests per second
175131.36 requests per second
178571.42 requests per second
179533.22 requests per second
181488.20 requests per second
182815.36 requests per second
183486.23 requests per second
redis-benchmark -P 32 -n 100000 geodecode 3471579339700058 withjson
69492.70 requests per second
74074.07 requests per second
74404.77 requests per second
74515.65 requests per second
76745.97 requests per second
77579.52 requests per second
78003.12 requests per second
78308.54 requests per second
78431.38 requests per second
78431.38 requests per second
redis-benchmark -P 32 -n 100000 georadiusbymember nyc 4545 20 km withdistance
99502.48 requests per second
108695.65 requests per second
109051.26 requests per second
109529.02 requests per second
110619.47 requests per second
111111.12 requests per second
112866.82 requests per second
114285.71 requests per second
115740.73 requests per second
116009.28 requests per second
redis-benchmark -P 32 -n 100000 georadiusbymember nyc 4545 20 km withgeojson
32310.18 requests per second
32520.32 requests per second
32530.91 requests per second
32615.79 requests per second
32626.43 requests per second
32637.08 requests per second
32959.79 requests per second
33333.33 requests per second
33355.57 requests per second
33467.20 requests per second
redis-benchmark -P 32 -n 100000 georadiusbymember nyc 4545 20 km withgeojsoncollection
36114.12 requests per second
36205.65 requests per second
36443.15 requests per second
36643.46 requests per second
36859.56 requests per second
37411.15 requests per second
37565.74 requests per second
37965.07 requests per second
37993.92 requests per second
38284.84 requests per second
redis-benchmark -P 32 -n 100000 georadiusbymember nyc 4545 20 km withgeojson withgeojsoncollection
20124.77 requests per second
20165.36 requests per second
20292.21 requests per second
20341.74 requests per second
20358.30 requests per second
20420.67 requests per second
20656.89 requests per second
20733.98 requests per second
20798.67 requests per second
20846.36 requests per second
Individual Benchmarks for Single Requests
% ./redis-benchmark -n 100000 "geoencode 40.7480973 -73.9564142"
====== geoencode 40.7480973 -73.9564142 ======
100000 requests completed in 0.83 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100.00% <= 0 milliseconds
119904.08 requests per second
% ./redis-benchmark -n 100000 "geodecode 1791875796750882"
====== geodecode 1791875796750882 ======
100000 requests completed in 0.83 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.97% <= 1 milliseconds
100.00% <= 1 milliseconds
121065.38 requests per second
% ./redis-benchmark -n 100000 geoadd nyc 40.7480973 -73.9564142 4545
====== geoadd nyc 40.7480973 -73.9564142 4545 ======
100000 requests completed in 0.89 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.86% <= 1 milliseconds
100.00% <= 1 milliseconds
112612.61 requests per second
% ./redis-benchmark -n 10000 geoadd nyc 40.7648057 -73.9733487 central park n/q/r 40.7362513 -73.9903085 union square 40.7126674 -74.0131604 wtc one 40.6428986 -73.7858139 jfk 40.7498929 -73.9375699 q4 40.7480973 -73.9564142 4545
====== geoadd nyc 40.7648057 -73.9733487 central park n/q/r 40.7362513 -73.9903085 union square 40.7126674 -74.0131604 wtc one 40.6428986 -73.7858139 jfk 40.7498929 -73.9375699 q4 40.7480973 -73.9564142 4545 ======
10000 requests completed in 0.10 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.99% <= 1 milliseconds
100.00% <= 1 milliseconds
102040.82 requests per second
% ./redis-benchmark -n 100000 georadiusbymember nyc 4545 5 km
====== georadiusbymember nyc 4545 5 km ======
100000 requests completed in 1.39 seconds
50 parallel clients
3 bytes payload
keep alive: 1
93.22% <= 1 milliseconds
99.95% <= 30 milliseconds
100.00% <= 30 milliseconds
71994.23 requests per second
% ./redis-benchmark -n 100000 georadiusbymember nyc 4545 20 km
====== georadiusbymember nyc 4545 20 km ======
100000 requests completed in 1.37 seconds
50 parallel clients
3 bytes payload
keep alive: 1
94.85% <= 1 milliseconds
100.00% <= 1 milliseconds
73046.02 requests per second
% ./redis-benchmark -n 10000 georadiusbymember nyc 4545 20 km withdistance
====== georadiusbymember nyc 4545 20 km withdistance ======
10000 requests completed in 0.17 seconds
50 parallel clients
3 bytes payload
keep alive: 1
94.29% <= 1 milliseconds
99.99% <= 2 milliseconds
100.00% <= 2 milliseconds
59171.60 requests per second
% redis-benchmark -n 10000 georadiusbymember nyc 4545 20 km withgeojson
====== georadiusbymember nyc 4545 20 km withgeojson ======
10000 requests completed in 0.47 seconds
50 parallel clients
3 bytes payload
keep alive: 1
0.03% <= 1 milliseconds
0.54% <= 2 milliseconds
99.69% <= 3 milliseconds
100.00% <= 3 milliseconds
21367.52 requests per second
% redis-benchmark -n 10000 georadiusbymember nyc 4545 20 km withgeojsoncollection
====== georadiusbymember nyc 4545 20 km withgeojsoncollection ======
10000 requests completed in 0.43 seconds
50 parallel clients
3 bytes payload
keep alive: 1
0.02% <= 1 milliseconds
6.19% <= 2 milliseconds
99.79% <= 3 milliseconds
100.00% <= 3 milliseconds
23474.18 requests per second
% redis-benchmark -n 10000 georadiusbymember nyc 4545 20 km withgeojson withgeojsoncollection
====== georadiusbymember nyc 4545 20 km withgeojson withgeojsoncollection ======
10000 requests completed in 0.74 seconds
50 parallel clients
3 bytes payload
keep alive: 1
0.01% <= 1 milliseconds
0.08% <= 2 milliseconds
0.33% <= 3 milliseconds
95.16% <= 4 milliseconds
100.00% <= 4 milliseconds
13477.09 requests per second
Pretty formatted version of the FeatureCollection. You can paste it directly into geojson.io or GeoJSONLint to see all the features on one map.
↩{ "features" : [ { "geometry" : { "coordinates" : [ -74.01316255331, 40.712667181451 ], "type" : "Point" }, "type" : "Feature", "properties" : { "distance" : 0, "member" : "wtc one", "set" : "nyc", "units" : "km" } }, { "geometry" : { "coordinates" : [ -73.990310132504, 40.736250227118 ], "type" : "Point" }, "type" : "Feature", "properties" : { "distance" : 3.2543954573357, "member" : "union square", "set" : "nyc", "units" : "km" } }, { "geometry" : { "coordinates" : [ -73.973347842693, 40.764806395699 ], "type" : "Point" }, "type" : "Feature", "properties" : { "distance" : 6.7000029092799, "member" : "central park n/q/r", "set" : "nyc", "units" : "km" } }, { "geometry" : { "coordinates" : [ -73.956412374973, 40.748097513816 ], "type" : "Point" }, "type" : "Feature", "properties" : { "distance" : 6.1975173818005, "member" : "4545", "set" : "nyc", "units" : "km" } }, { "geometry" : { "coordinates" : [ -73.945495784283, 40.747532270998 ], "type" : "Point" }, "type" : "Feature", "properties" : { "distance" : 6.8968709532079, "member" : "lic market", "set" : "nyc", "units" : "km" } } ], "type" : "FeatureCollection" }