如何实现查找附近的人-GEO

背景

打开美团,可以通过自身定位查看附近的商品。打开社交软件,可以查看附近的人交友。打开滴滴,可以查看的附近的共享单车,那这些是如何实现?

Redis GEO

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。

Redis GEO 操作方法有:

  • geoadd:添加地理位置的坐标。
  • geopos:获取地理位置的坐标。
  • geodist:计算两个位置之间的距离。
  • georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
  • georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
  • geohash:返回一个或多个位置对象的 geohash 值。
  • zrem:删除地理位置

基础使用

数据准备

先用百度地图提供的经纬度查询工具 拾取坐标系统,准备一些坐标信息:

选择三个点的坐标作为测数据,如下

地点 坐标
翠湖公园(a) 102.710039,25.054179
大观公园(b) 102.679209,25.027989
动物园(c) 102.714992,25.061773

添加地理位置

geoadd

geoadd 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。

geoadd 语法格式如下:

sql 复制代码
GEOADD key longitude latitude member [longitude latitude member ...]

重点参数说明如下:

  • longitude 表示经度
  • latitude 表示纬度
  • member 是为此经纬度起的名字

此命令支持一次添加一个或多个位置信息。

以下实例中key为km

arduino 复制代码
geoadd km  102.710039 25.054179 "a"
geoadd km  102.679209 25.027989  "b"
geoadd km  102.714992 25.061773  "c"

查询位置信息

geopos

geopos 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。

geopos 语法格式如下:

sql 复制代码
GEOPOS key member [member ...]

此命令支持查看一个或多个位置信息

ruby 复制代码
127.0.0.1:6379> geopos km a
1) 1) "102.69033282995223999"
   2) "25.04997624927904809"

距离统计

geodist

geodist 用于返回两个给定位置之间的距离。

geodist 语法格式如下:

css 复制代码
GEODIST key member1 member2 [m|km|ft|mi]

member1 member2 为两个地理位置。

最后一个距离单位参数说明:

m :米,默认单位。

km :千米。

mi :英里。

ft :英尺。

ruby 复制代码
# 计算翠湖公园(a)到大观公园(b)的距离,单位km
127.0.0.1:6379> geodist km a b km
"4.2587"

计算翠湖公园(a)到大观公园(b)的距离是4.25公里,跟地图比对,结果基本吻合

查询某位置内的其他成员信息

georadius

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

georadiusbymember

georadiusbymember 和 georadius 命令一样, 都可以找出位于指定范围内的元素, 但是 georadiusbymember 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。georadius 与 georadiusbymember 语法格式如下:

css 复制代码
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

参数说明:

  • m 米,默认单位。
  • km :千米。
  • mi :英里。
  • t :英尺。
  • WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
  • WITHCOORD: 将位置元素的经度和纬度也一并返回。
  • WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
  • COUNT 限定返回的记录数。
  • ASC: 查找结果根据距离从近到远排序。
  • DESC: 查找结果根据从远到近排序。

根据给定的经纬度坐标查询附近的成员

ruby 复制代码
# 根据查询的坐标,查询5km以内的成员信息
127.0.0.1:6379> georadius km 102.705224 25.0499233 5 km
1) "b"
2) "a"
3) "c"

# 根据坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离
127.0.0.1:6379> georadius km 102.705224 25.0499233 5 km withdist
1) 1) "b"
   2) "3.5812"
2) 1) "a"
   2) "0.6776"
3) 1) "c"
   2) "1.6450"
   
#根据坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离,从近到远,返回2个成员
georadius km 102.705224 25.0499233 5 km withdist count 2 asc
1) 1) "a"
   2) "0.6776"
2) 1) "c"
   2) "1.6450"
# 根据成员的坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离,从近到远,返回2个成员
  127.0.0.1:6379> georadiusbymember km a  5 km withdist count 2 asc
1) 1) "a"
   2) "0.0000"
2) 1) "c"
   2) "0.9813" 

删除地理位置

zrem

删除地理位置信息

sql 复制代码
zrem key member [member ...]

比如c成员的坐标信息

ruby 复制代码
127.0.0.1:6379> zrem km c
(integer) 1

场景实践

引入jedis

xml 复制代码
<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.1.0</version>
        </dependency>

附近的门店

查找附近的门店,本地生活这种场景特别多。

我想喝一杯瑞幸咖啡,打开瑞幸的微信小程序,根据自身当前定位,查看下我附近都有哪些门店,哪个离我最近,那我就在哪个门店下单。

实现思路

  1. 门店的地址固定的,除非搬迁或者闭店,才会发生改变,根据门店地址换算为经纬度存储在redis
  2. 根据gps定位,获取自身当前的经纬度
  3. 根据定位的经纬度查询附近的门店

代码实现

java 复制代码
public static void main(String[] args) {
        Jedis jedis = new Jedis("10.1.250.157",6379);
        jedis.auth("google00");
        //添加门店a
        jedis.geoadd("store",102.710039,25.054179,"a");
        //添加门店b
        jedis.geoadd("store",102.679209,25.027989,"b");
        //添加门店c
        jedis.geoadd("store",102.714992 ,25.061773,"c");

        //我当前定位的坐标,查询附近的门店,从近到远的门店,并显示距离
        GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
        geoRadiusParam.withDist().sortAscending();
        List<GeoRadiusResponse>  storeList =  jedis.georadius("store",102.705224 ,25.0499233,5,GeoUnit.KM,geoRadiusParam);
        for (GeoRadiusResponse r : storeList) {
            System.out.println("门店"+r.getMemberByString()+" 距离我:"+r.getDistance()+"km");
        }
    }

结果:

less 复制代码
门店a 距离我:0.6776km
门店c 距离我:1.645km
门店b 距离我:3.5812km

注意点:所有门店数据放入到store这个key里,查询如果频繁,这个key容易成为热key,可以使用redis集群,同样的数据多设置几个key,比如:store_1,store_2,store_3 ,查询的时候可以根据用户id取余,命中不同的key查询

附近的人

社交软件,经常看到附近的人,怎么做呢

实现思路

  1. 打开软件,当点击附近的人,定位自身的坐标
  2. 根据自身的坐标,去查询附近的附近的人

代码实现

java 复制代码
 public static void main(String[] args) {
        Jedis jedis = new Jedis("10.1.250.157",6379);
        jedis.auth("google00");
        //添加张三
        jedis.geoadd("person_member",102.710039,25.054179,"zhangsan");
        //添加小明
        jedis.geoadd("person_member",102.679209,25.027989,"xiaoming");
        //添加李四
        jedis.geoadd("person_member",102.714992 ,25.061773,"lisi");

        //我当前定位的坐标,查询附近5km范围内的人,从近到远排序,并显示距离
        GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
        geoRadiusParam.withDist().sortAscending();
        List<GeoRadiusResponse>  storeList =  jedis.georadius("person_member",102.705224 ,25.0499233,5,GeoUnit.KM,geoRadiusParam);
        for (GeoRadiusResponse r : storeList) {
            System.out.println(r.getMemberByString()+" 距离我:"+r.getDistance()+"km");
        }
    }

结果

复制代码
zhangsan 距离我:0.6776km
lisi 距离我:1.645km
xiaoming 距离我:3.5812km

计算距离

计算两个坐标之间的距离

代码实现

java 复制代码
public static void main(String[] args) {
        Jedis jedis = new Jedis("10.1.250.157",6379);
        jedis.auth("google00");
           //添加张三
        jedis.geoadd("person_member",102.710039,25.054179,"zhangsan");
        //添加小明
        jedis.geoadd("person_member",102.679209,25.027989,"xiaoming");
        //添加李四
        jedis.geoadd("person_member",102.714992 ,25.061773,"lisi");

        //我当前定位的坐标,查询附近5km的人,从近到远排序,并显示距离
        GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
        geoRadiusParam.withDist().sortAscending();
        Double dist = jedis.geodist("person_member","xiaoming","lisi",GeoUnit.KM);
        System.out.println("xiaoming与lisi距离:"+dist+"km");
    }

结果

复制代码
zhangsan与lisi距离:5.208km

写作不易,刚好你看到,刚好对你有帮助,动动小手,点点赞,欢迎转发,有疑问的欢迎留言或者私信讨论,有问必回。

相关推荐
hnlucky34 分钟前
redis 数据类型新手练习系列——Hash类型
数据库·redis·学习·哈希算法
丘山子39 分钟前
一些鲜为人知的 IP 地址怪异写法
前端·后端·tcp/ip
CopyLower1 小时前
在 Spring Boot 中实现 WebSockets
spring boot·后端·iphone
天天扭码2 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑2 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
AnsenZhu2 小时前
2025年Redis分片存储性能优化指南
数据库·redis·性能优化·分片
追逐时光者3 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
天天扭码3 小时前
深入讲解Javascript中的常用数组操作函数
前端·javascript·面试
方圆想当图灵3 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven