learn-tech/专栏/Redis核心原理与实战/20查询附近的人——GEO.md
2024-10-16 06:37:41 +08:00

6.9 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        20 查询附近的人——GEO
                        受过高等教育的我们都知道,我们所处的任何位置都可以用经度和纬度来标识,经度的范围 -180 到 180纬度的范围为 -90 到 90。纬度以赤道为界赤道以南为负数赤道以北为正数经度以本初子午线英国格林尼治天文台为界东边为正数西边为负数。

Redis 在 3.2 版本中增加了 GEO 类型用于存储和查询地理位置,关于 GEO 的命令不多,主要包含以下 6 个:

geoadd添加地理位置 geopos查询位置信息 geodist距离统计 georadius查询某位置内的其他成员信息 geohash查询位置的哈希值 zrem删除地理位置

下面我们分别来看这些命令的使用。

基础使用

添加地理位置

我们先用百度地图提供的经纬度查询工具,地址:

http://api.map.baidu.com/lbsapi/getpoint/index.html

如下图所示:

找了以下 4 个地点,添加到 Redis 中:

天安门116.404269,39.913164 月坛公园116.36,39.922461 北京欢乐谷116.499705,39.874635 香山公园116.193275,39.996348

代码如下:

127.0.0.1:6379> geoadd site 116.404269 39.913164 tianan (integer) 1 127.0.0.1:6379> geoadd site 116.36 39.922461 yuetan (integer) 1 127.0.0.1:6379> geoadd site 116.499705 39.874635 huanle (integer) 1 127.0.0.1:6379> geoadd site 116.193275 39.996348 xiangshan (integer) 1

相关语法:

geoadd key longitude latitude member [longitude latitude member ...]

重点参数说明如下:

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

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

查询位置信息

127.0.0.1:6379> geopos site tianan

    1. "116.40541702508926392"
    2. "39.91316289865137179"

相关语法:

geopos key member [member ...]

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

距离统计

127.0.0.1:6379> geodist site tianan yuetan km "3.9153"

可以看出天安门距离月坛公园的直线距离大概是 3.9 km我们打开地图使用工具测试一下咱们的统计结果是否准确如下图所示

可以看出 Redis 的统计和使用地图工具统计的距离是完全吻合的。

注意:此命令统计的距离为两个位置的直线距离。

相关语法:

geodist key member1 member2 [unit]

unit 参数表示统计单位,它可以设置以下值:

m以米为单位默认单位 km以千米为单位 mi以英里为单位 ft以英尺为单位。

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

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km

  1. "tianan"
  2. "yuetan"

此命令的意思是查询天安门116.405419,39.913164)附近 5 公里范围内的成员列表。

相关语法:

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

可选参数说明如下。

  1. WITHCOORD

说明:返回满足条件位置的经纬度信息。

示例代码:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km withcoord

    1. "tianan"
      1. "116.40426903963088989"
      2. "39.91316289865137179"
    1. "yuetan"
      1. "116.36000186204910278"
      2. "39.92246025586381819"
  1. WITHDIST

说明:返回满足条件位置与查询位置的直线距离。

示例代码:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km withdist

    1. "tianan"
    2. "0.0981"
    1. "yuetan"
    2. "4.0100"
  1. WITHHASH

说明:返回满足条件位置的哈希信息。

示例代码:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km withhash

    1. "tianan"
    2. (integer) 4069885552230465
    1. "yuetan"
    2. (integer) 4069879797297521
  1. COUNT count

说明:指定返回满足条件位置的个数。

例如,指定返回一条满足条件的信息,代码如下:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km count 1

  1. "tianan"
  1. ASC|DESC

说明:从近到远|从远到近排序返回。

示例代码:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km desc

  1. "yuetan"
  2. "tianan" 127.0.0.1:6379> georadius site 116.405419 39.913164 5 km asc
  3. "tianan"
  4. "yuetan"

当然以上这些可选参数也可以一起使用,例如以下代码:

127.0.0.1:6379> georadius site 116.405419 39.913164 5 km withdist desc

    1. "yuetan"
    2. "4.0100"
    1. "tianan"
    2. "0.0981"
  1. 查询哈希值

127.0.0.1:6379> geohash site tianan

  1. "wx4g0cgp000"

相关语法:

geohash key member [member ...]

此命令支持查询一个或多个地址的哈希值。

  1. 删除地理位置

127.0.0.1:6379> zrem site xiaoming (integer) 1

相关语法:

zrem key member [member ...]

此命令支持删除一个或多个位置信息。

代码实战

下面我们用 Java 代码,来实现查询附近的人,完整代码如下:

import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.GeoRadiusResponse; import redis.clients.jedis.GeoUnit; import redis.clients.jedis.Jedis;

import java.util.HashMap; import java.util.List; import java.util.Map;

public class GeoHashExample { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); Map<String, GeoCoordinate> map = new HashMap<>(); // 添加小明的位置 map.put("xiaoming", new GeoCoordinate(116.404269, 39.913164)); // 添加小红的位置 map.put("xiaohong", new GeoCoordinate(116.36, 39.922461)); // 添加小美的位置 map.put("xiaomei", new GeoCoordinate(116.499705, 39.874635)); // 添加小二 map.put("xiaoer", new GeoCoordinate(116.193275, 39.996348)); jedis.geoadd("person", map); // 查询小明和小红的直线距离 System.out.println("小明和小红相距:" + jedis.geodist("person", "xiaoming", "xiaohong", GeoUnit.KM) + " KM"); // 查询小明附近 5 公里的人 List res = jedis.georadiusByMemberReadonly("person", "xiaoming", 5, GeoUnit.KM); for (int i = 1; i < res.size(); i++) { System.out.println("小明附近的人:" + res.get(i).getMemberByString()); } } }

以上程序执行的结果如下:

小明和小红相距3.9153 KM 小明附近的人xiaohong

应用场景

Redis 中的 GEO 经典使用场景如下:

查询附近的人、附近的地点等; 计算相关的距离信息。

小结

GEO 是 Redis 3.2 版本中新增的功能,只有升级到 3.2+ 才能使用GEO 本质上是基于 ZSet 实现的,这点在 Redis 源码找到相关信息,我们可以 GEO 使用实现查找附近的人或者附近的地点,还可以用它来计算两个位置相隔的直线距离。