Databases 18 min read

How Redis Powers ‘Nearby People’ with GEOADD and GEORADIUS: A Deep Source‑Code Dive

This article explains how Redis implements the “nearby people” feature using its Geo module, detailing the GEOADD and GEORADIUS commands, their underlying algorithms, source‑code flow, and the resulting time‑complexity for adding and querying location data.

Programmer DD
Programmer DD
Programmer DD
How Redis Powers ‘Nearby People’ with GEOADD and GEORADIUS: A Deep Source‑Code Dive
Redis Geo illustration
Redis Geo illustration

Redis provides a high‑performance solution for location‑based services such as “nearby people” by combining ordered sets (zset) with geohash encoding, offering both add and query capabilities.

Operation Commands

GEOADD : add a member with longitude, latitude and name to a key.

GEOPOS : return the coordinates of given members.

GEODIST : return the distance between two members.

GEOHASH : return the geohash representation of members.

GEORADIUS : return members within a radius of a given point.

GEORADIUSBYMEMBER : return members within a radius of a given member.

Combining GEOADD and GEORADIUS implements the basic “add” and “search” functions of a nearby‑people service.

GEOADD

Usage

GEOADD key longitude latitude member [longitude latitude member ...]

The command stores each location object as a member of a sorted set, where the score is a 52‑bit geohash derived from the latitude and longitude. The return value is the number of elements successfully added.

Source‑code analysis

/* GEOADD key long lat name [long2 lat2 name2 ... longN latN nameN] */
void geoaddCommand(client *c) {
    // argument validation
    if ((c->argc - 2) % 3 != 0) {
        addReplyError(c, "syntax error. Try GEOADD key [x1] [y1] [name1] ...");
        return;
    }
    int elements = (c->argc - 2) / 3;
    int argc = 2 + elements * 2; // ZADD key score ele ...
    robj **argv = zcalloc(argc * sizeof(robj*));
    argv[0] = createRawStringObject("zadd",4);
    argv[1] = c->argv[1]; incrRefCount(argv[1]);
    for (int i = 0; i < elements; i++) {
        double xy[2];
        if (extractLongLatOrReply(c,(c->argv+2)+(i*3),xy) == C_ERR) {
            for (int j = 0; j < argc; j++) if (argv[j]) decrRefCount(argv[j]);
            zfree(argv);
            return;
        }
        GeoHashBits hash; geohashEncodeWGS84(xy[0],xy[1],GEO_STEP_MAX,&hash);
        GeoHashFix52Bits bits = geohashAlign52Bits(hash);
        robj *score = createObject(OBJ_STRING,sdsfromlonglong(bits));
        robj *val = c->argv[2 + i*3 + 2];
        argv[2+i*2] = score;
        argv[3+i*2] = val; incrRefCount(val);
    }
    replaceClientCommandVector(c,argc,argv);
    zaddCommand(c);
}

The implementation converts latitude/longitude to a 52‑bit geohash, builds a ZADD argument vector, and delegates the actual insertion to the existing sorted‑set command.

GEORADIUS

Usage

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] [STORE key] [STOREDIST key]

The command returns all members whose distance from the given point does not exceed the specified radius. Optional flags control the output format and sorting.

Source‑code analysis (excerpt)

void georadiusGeneric(client *c, int flags) {
    robj *key = c->argv[1];
    robj *storekey = NULL;
    int stoRedis = 0; // 0 for STORE, 1 for STOREDIST
    // lookup the sorted set
    robj *zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp]);
    if (zobj == NULL || checkType(c,zobj,OBJ_ZSET)) return;
    // parse center point (coordinates or member)
    double xy[2] = {0};
    int base_args;
    if (flags & RADIUS_COORDS) {
        // extract longitude/latitude from arguments
    } else {
        // resolve member to coordinates
    }
    // extract radius and convert to meters
    double radius_meters = 0, conversion = 1;
    if ((radius_meters = extractDistanceOrReply(c,c->argv+base_args-2,&conversion)) < 0) return;
    // parse optional flags (WITHDIST, WITHHASH, WITHCOORD, ASC/DESC, COUNT, STORE, STOREDIST)
    int withdist = 0, withhash = 0, withcoords = 0, sort = SORT_NONE;
    long count = 0;
    // ... flag handling omitted for brevity ...
    // compute the geohash area that covers the search circle
    GeoHashRadius georadius = geohashGetAreasByRadiusWGS84(xy[0],xy[1],radius_meters);
    // search the nine neighboring geohash boxes
    geoArray *ga = geoArrayCreate();
    membersOfAllNeighbors(zobj,georadius,xy[0],xy[1],radius_meters,ga);
    if (ga->used == 0 && storekey == NULL) {
        addReplyNull(c);
        geoArrayFree(ga);
        return;
    }
    // ... build reply based on flags ...
    geoArrayFree(ga);
}

The algorithm first determines the smallest geohash precision that fully covers the search circle, then creates a 3×3 grid (the central cell plus eight neighbors). For each cell it finds the range of geohash scores, retrieves matching members from the sorted set, decodes their coordinates, and finally filters by the exact spherical distance.

Algorithm Summary

Both commands share a common workflow:

Validate and extract arguments.

Convert geographic coordinates to a 52‑bit geohash (the sorted‑set score).

For GEORADIUS, compute the covering geohash grid, iterate the nine cells, and use the ordered‑set range query (O(log M)) to collect candidates, then apply precise distance checks (O(N)).

The overall time complexity of a nearby‑people query is O(N + log M), where N is the number of elements within the radius and M is the number of elements examined in the nine‑cell grid.

Because Redis stores data entirely in memory and leverages the efficient skip‑list implementation of sorted sets, the Geo module delivers very high performance for location‑based add and search operations.

Geohash grid illustration
Geohash grid illustration
Geohash precision illustration
Geohash precision illustration
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

algorithmdatabaseRedisGEOADDGEOLocation SearchGEORADIUS
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.