Backend Development 14 min read

Implementing Nearby‑People (LBS) with MySQL and Redis GEO

This article explains how to build a location‑based "nearby people" feature by storing coordinates in MySQL, filtering with rectangular bounds, calculating distances in Java, and then scaling the solution with Redis GEO using GeoHash and Sorted Sets for high‑performance proximity queries.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Implementing Nearby‑People (LBS) with MySQL and Redis GEO

The piece starts by describing the concept of LBS (Location Based Services) where "nearby people" are identified based on the user's current latitude and longitude.

It first shows a simple MySQL approach: a table nearby_user stores id, name, longitude, latitude and creation time, and a composite index on (longitude, latitude) is created to speed up range queries.

CREATE TABLE `nearby_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL COMMENT '名称',
  `longitude` double DEFAULT NULL COMMENT '经度',
  `latitude` double DEFAULT NULL COMMENT '纬度',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

To limit the search space, the article suggests drawing a square that encloses a circle of radius (e.g., 1000 m) and filtering users whose coordinates fall inside that rectangle, then discarding those whose true distance exceeds the radius.

The Java implementation demonstrates how to compute the bounding rectangle, fetch users within it, and filter by exact distance:

/**
 * 获取附近 x 米的人
 *
 * @param distance 搜索距离范围 单位km
 * @param userLng  当前用户的经度
 * @param userLat  当前用户的纬度
 */
public String nearBySearch(double distance, double userLng, double userLat) {
  //1.获取外接正方形
  Rectangle rectangle = getRectangle(distance, userLng, userLat);
  //2.获取位置在正方形内的所有用户
  List
users = userMapper.selectUser(rectangle.getMinX(), rectangle.getMaxX(), rectangle.getMinY(), rectangle.getMaxY());
  //3.剔除半径超过指定距离的多余用户
  users = users.stream()
    .filter(a -> getDistance(a.getLongitude(), a.getLatitude(), userLng, userLat) <= distance)
    .collect(Collectors.toList());
  return JSON.toJSONString(users);
}

private Rectangle getRectangle(double distance, double userLng, double userLat) {
  return spatialContext.getDistCalc()
    .calcBoxByDistFromPt(spatialContext.makePoint(userLng, userLat), 
      distance * DistanceUtils.KM_TO_DEG, spatialContext, null);
}

/***
 * 球面中,两点间的距离
 * @param longitude 经度1
 * @param latitude  纬度1
 * @param userLng   经度2
 * @param userLat   纬度2
 * @return 返回距离,单位km
 */
private double getDistance(Double longitude, Double latitude, double userLng, double userLat) {
  return spatialContext.calcDistance(spatialContext.makePoint(userLng, userLat),
    spatialContext.makePoint(longitude, latitude)) * DistanceUtils.DEG_TO_KM;
}

The corresponding SQL query is straightforward:

SELECT * FROM nearby_user
WHERE 1=1
AND (longitude BETWEEN #{minlng} AND #{maxlng})
AND (latitude BETWEEN #{minlat} AND #{maxlat});

Because a pure MySQL solution may become a bottleneck under high concurrency, the article explores Redis alternatives.

It first evaluates Redis Hash, noting that while a hash can store userId → longitude,latitude , it cannot efficiently perform range queries or sorting.

Redis Sorted Set is then introduced as a better fit because it stores members with a numeric score that can be sorted. By encoding latitude and longitude into a single numeric value using GeoHash, the Sorted Set can serve as the underlying structure for Redis' GEO commands.

The GeoHash algorithm recursively bisects the longitude (and latitude) range, assigning a binary digit at each step, ultimately producing an integer that preserves spatial proximity.

Combining the longitude and latitude bits yields a final GeoHash code (e.g., 1010011011 ) that can be used as the score in a Sorted Set.

Redis GEO commands are then presented. GEOADD stores a member with its longitude and latitude:

GEOADD girl:location 13.361389 38.115556 "苍井空" 15.087269 37.502669 "波多野结衣"

GEORADIUS retrieves members within a radius around a given point, optionally sorting by distance and limiting the count:

GEORADIUS girl:location 15.087269 37.502669 10 km ASC COUNT 10

To remove a member when a user goes offline, the standard ZREM command works because GEO is built on Sorted Sets:

ZREM girl:location "苍井空"

Finally, the article advises deployment considerations: keep GEO data in a dedicated Redis cluster, split data by region when the size exceeds ~1 M entries per key, and be aware of migration overhead in clustered environments.

Overall, the guide walks through a complete backend implementation of an LBS "nearby people" feature, from relational storage to high‑performance geospatial indexing with Redis.

backendRedisMySQLGeoHashLBSLocation Services
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login 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.