Redis的GEO的使用以及应用场景

在日常生活中,我们越来越依赖搜索“附近的餐馆”、在打车软件上叫车,这些都离不开基于位置信息服务(Location-Based Service,LBS)的应用。LBS 应用访问的数据是和人或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 就非常适合应用在 LBS 服务的场景中。

其实这个服务用es也可以做,但是学习以及维护成本太高了,因此我们使用Redis来做。

GEO原理

GEO使用的是Sorted Set集合类型,它底层并没有设计新的数据结构。GEO类型使用GeoHash编码方法实现了经纬度到Sorted Set中元素权重分数的转换,这其中的两个关键机制就是对二维地图做区间划分对区间进行编码。一旦经纬度落在某个区间后,就用区间的编码来表示,并把编码值作为Sorted Set元素的权重分数。

这样一来,我们就可以把经纬度保存到Sorted Set中,利用Sorted Set提供的按权重进行有序范围查找的特性,实现LBS服务中频繁使用的搜索附近的需求。

常用命令

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

# 从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
GEOPOS key member [member ...]

# 返回两个给定位置之间的距离。
GEODIST key member1 member2 [m|km|ft|mi]

# 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

应用场景

滴滴叫车

这里以滴滴叫车的场景为例子,介绍一下具体如何使用GEO命令:GEOADDGEORADIUS这两个命令。

假设车辆ID是33,经纬度位置是(116.04579,39.030452),我们可以利用好一个GEO集合保存所有车辆的经纬度,集合key是cars:locations。

执行下面的这个命令,就可以把ID号为33号的车辆当前经纬度位置存入GEO集合中:

GEOADD cars:locations 116.034579 39.030452 33

当用户想要寻找自己附近的网约车时,LBS应用就可以使用GEORADIUS梦里。

例如,LBS应用执行下面的命令时,Redis会根据输入的用户的经纬度信息(116.05479,39.030452),查找以这个经纬度为中心的5公里内的车辆信息,并返回给LBS应用。

GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10

代码实现

func (u *UserService) Location(ctx context.Context, request *LocationRequest) (*Response, error) {
   longitude := request.Longitude
   o, err := strconv.ParseFloat(longitude, 64)
   if err != nil {
      log.Println(err)
   }
   latitude := request.Latitude
   a, err := strconv.ParseFloat(latitude, 64)
   if err != nil {
      log.Println(err)
   }
   userId := request.UserId
   location := request.Location
   s := strconv.FormatInt(userId, 10)
   key := common.UserLocation + s
   dao.Rdb.GeoAdd(dao.RCtx, key, &redis.GeoLocation{
      Name:      location,
      Longitude: o,
      Latitude:  a,
   })
   return &Response{
      Status: http.StatusOK,
      Msg:    "插入信息成功",
   }, nil
}

func (u *UserService) FindFriend(ctx context.Context, request *FindFriendRequest) (*FindFriendResponse, error) {
   longitude := request.Longitude
   latitude := request.Latitude
   o, err := strconv.ParseFloat(longitude, 64)
   if err != nil {
      log.Println(err)
   }
   a, err := strconv.ParseFloat(latitude, 64)
   if err != nil {
      log.Println(err)
   }
   key := common.UserLocation
   result, err := dao.Rdb.GeoRadius(dao.RCtx, key, o, a, &redis.GeoRadiusQuery{
      Radius:      5,
      Unit:        "km",
      WithCoord:   false, //传入WITHCOORD参数,则返回结果会带上匹配位置的经纬度
      WithDist:    true,  //传入WITHDIST参数,则返回结果会带上匹配位置与给定地理位置的距离。
      WithGeoHash: false, //传入WITHHASH参数,则返回结果会带上匹配位置的hash值
      Sort:        "ASC", //默认结果是未排序的,传入ASC为从近到远排序,传入DESC为从远到近排序。
   }).Result()
   if err != nil {
      log.Println(err)
   }
   N := len(result)
   name := make([]string, N)
   dist := make([]float32, N)
   for i := 0; i < N; i++ {
      name[i] = result[i].Name
      dist[i] = float32(result[i].Dist)
   }
   return &FindFriendResponse{
      Name: name,
      Dist: dist,
   }, nil
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡桃姓胡,蝴蝶也姓胡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值