Redis中的GEO类型是什么?如何基于Redis进行地理位置存储与查询


想象一下,你正在开发一个需要实时地理位置服务的应用,比如一个共享单车平台或是一个外卖配送系统。如何在海量的地理数据中快速查找指定范围内的所有点?如何高效地计算两个位置之间的距离?又或者你思考过网约车打车时是如何找寻到附近的车辆吗?

本篇文章将探讨 Redis 的 GEO 类型的基本认识及使用。

什么是GEO

Redis中的Geo类型是用于存储地理位置数据的特殊数据结构。它允许你存储地理坐标(经纬度)并对这些坐标执行各种操作,如计算距离、查找附近的位置等。Geo类型是在Redis 3.2版本中引入的。

Geo类型在Redis中是基于有序集合(Sorted Set)实现的。它使用Geohash编码将经纬度转换为可以排序的字符串,然后存储为有序集合中的分数。这样就可以利用有序集合的特性来实现地理位置相关的操作。至于Geohash是什么,后续将会形成一片文章来对其做具体介绍,现在你只需要明白,Redis中的Geo类型能够进行计算距离、查找附近的位置等功能即可。

GEO的常用操作

  1. GEOADD

    • 用于将地理空间位置(经纬度)添加到指定的key中。
    • 语法: GEOADD key longitude latitude member [longitude latitude member ...]
    • 示例: GEOADD cities 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
  2. GEOPOS

    • 返回指定成员的经纬度(可以有多个)。
    • 语法: GEOPOS key member [member ...]
    • 示例: GEOPOS cities "Palermo" "Catania"
  3. GEODIST

    • 计算两个成员之间的距离,可以指定返回的单位(米、千米、英里、英尺)。
    • 语法: GEODIST key member1 member2 [unit]
    • 示例: GEODIST cities "Palermo" "Catania" km
  4. GEORADIUS

    • 查找位于指定圆心和半径范围内的所有成员。返回的结果可以按距离排序,并可以选择返回距离或地理位置。
    • 语法: GEORADIUS key longitude latitude radius unit [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    • 示例: GEORADIUS cities 15 37 200 km WITHDIST
  5. GEORADIUSBYMEMBER

    • 类似于GEORADIUS,但圆心是由给定的成员来指定的。
    • 语法: GEORADIUSBYMEMBER key member radius unit [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    • 示例: GEORADIUSBYMEMBER cities "Palermo" 100 km
  6. GEOHASH

    • 返回一个或多个成员的Geohash表示。
    • 语法: GEOHASH key member [member ...]
    • 示例: GEOHASH cities "Palermo" "Catania"

场景举例

我们以网约车场景举例来说明GEO类型常用的操作,这个场景里有两位司机分别为张师傅和王师傅,以及一位打车的小王同学。

三个人的空间位置关系如图所示:
在这里插入图片描述

其对应的GeoJson数据如下:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "driver": "张师傅"
      },
      "geometry": {
        "coordinates": [
          116.3308032723209,
          39.94238054837655
        ],
        "type": "Point"
      },
      "id": 0
    },
    {
      "type": "Feature",
      "properties": {
        "driver": "李师傅"
      },
      "geometry": {
        "coordinates": [
          116.39043340452554,
          39.925354295445146
        ],
        "type": "Point"
      },
      "id": 1
    },
    {
      "type": "Feature",
      "properties": {
        "user": "小王",
        "marker-color": "#e30202",
        "marker-size": "medium",
        "marker-symbol": "circle"
      },
      "geometry": {
        "coordinates": [
          116.35007287314164,
          39.922752082291424
        ],
        "type": "Point"
      },
      "id": 2
    }
  ]
}

Java代码示例

使用Jedis作为操作Redis的客户端。

class RedisGeo{
	@Test  
	public void geoBaseApi(){  
	    String DRIVERS_REDIS_KEY = "drivers";  
	    // 添加师傅所在位置  
	    jedis.geoadd(DRIVERS_REDIS_KEY,116.3308032723209 ,39.94238054837655,"张师傅");  
	    jedis.geoadd(DRIVERS_REDIS_KEY,116.39043340452554 ,39.925354295445146,"王师傅");  
	    // 小王同学所在位置经纬度  
	    Double userWangLng = 116.35007287314164;  
	    Double userWangLat = 39.922752082291424;  
	    // 查询张师傅所在位置,可同时查询多个member的位置,所以返回值为List  
	    List<GeoCoordinate> geoposOfZhang = jedis.geopos(DRIVERS_REDIS_KEY, "张师傅");  
	    for (GeoCoordinate geoCoordinate : geoposOfZhang) {  
	        log.info("张师傅所在位置,经度:{},纬度:{}",geoCoordinate.getLongitude(),geoCoordinate.getLatitude());  
	    }  
	    // 计算张师傅和王师傅之间的距离,GeoUnit 不赋值,默认为米  
	    Double geodist = jedis.geodist(DRIVERS_REDIS_KEY, "张师傅", "王师傅",GeoUnit.KM);  
	    log.info("张师傅和王师傅的距离为:{}KM",geodist);  
	    // 查找小王同学附近的司机  
	    List<GeoRadiusResponse> drivers = jedis.georadius(DRIVERS_REDIS_KEY, userWangLng, userWangLat, 5, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist());  
	    for (GeoRadiusResponse geoRadiusResponse : drivers) {  
	        log.info("小王同学附近有司机:{},距离小王同学的距离为:{}KM",geoRadiusResponse.getMemberByString(),geoRadiusResponse.getDistance());  
	    }  
	    // 以王师傅为中心,查找王师傅附近5KM的司机  
	    List<GeoRadiusResponse> nearbyDrivers = jedis.georadiusByMember(DRIVERS_REDIS_KEY, "王师傅", 5, GeoUnit.KM,GeoRadiusParam.geoRadiusParam().withDist());  
	    for (GeoRadiusResponse geoRadiusResponse : drivers) {  
	        log.info("王师傅附近的司机:{},距离王师傅的距离为:{}KM",geoRadiusResponse.getMemberByString(),geoRadiusResponse.getDistance());  
	    }  
	}
}

一些类的介绍

GeoRadiusParam

在上述示例代码中,有这样的一行代码:

// 查找小王同学附近的司机  
List<GeoRadiusResponse> drivers = jedis.georadius(DRIVERS_REDIS_KEY, userWangLng, userWangLat, 5, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist()); 

这里的参数GeoRadiusParam.geoRadiusParam().withDist()的作用就是返回距离相关信息。在使用 Redis 的 Geo 命令时,GEORADIUSGEORADIUSBYMEMBER 支持多种选项来定制查询结果。如果你没有明确要求返回距离信息,默认情况下,GeoRadiusResponse 中的距离会返回为 0。

GeoRadiusParam 是一个辅助类,用于构建 GEORADIUSGEORADIUSBYMEMBER 命令的参数。这使得查询更加灵活和可定制。

常用方法

  • withDist(): 指定返回结果中包含距离。
  • withCoord(): 指定返回结果中包含坐标。
  • sortAscending(): 按距离升序排序结果。
  • sortDescending(): 按距离降序排序结果。
  • count(int count): 限制返回结果的数量。

GeoRadiusResponse

GeoRadiusResponse 是一个类,用于封装 GEORADIUSGEORADIUSBYMEMBER 命令的结果。每个 GeoRadiusResponse 实例代表一个查询结果。

主要方法

  • getMemberByString(): 返回成员名称,作为字符串。
  • getDistance(): 返回距离(如果查询中包含 WITHDIST)。
  • getCoordinate(): 返回坐标(如果查询中包含 WITHCOORD)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值