• redis之GEO使用


    写在前面

    LBS(location based service)应用,如滴滴打车应用,需要根据用户的位置信息来获取某些数据,如获取距离当前用户指定距离范围内的所有车辆信息,该类的应用就可以使用本文我们要学习的GEO了,接下来一起看下。

    1:实战

    1.1:geoadd

    添加位置信息,格式GEOADD key longitude latitude member [longitude latitude member ...],可以指定多组经度+纬度+位置名称,如下:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    
    • 1
    • 2

    1.2:geopos

    获取指定位置的经纬度,如果是不存在的位置则返回nil,格式GEOPOS key member [member ...],如下测试:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    redis> GEOPOS Sicily Palermo Catania NonExisting
    1) 1) "13.36138933897018433"
       2) "38.11555639549629859"
    2) 1) "15.08726745843887329"
       2) "37.50266842333162032"
    3) (nil)
    redis>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.3:geodist

    geodist 用于返回两个给定位置之间的距离。geodist 语法格式为GEODIST key member1 member2 [m|km|ft|mi],参数说明如下:

    m:米,默认单位。
    km:千米。
    mi:英里。
    ft:英尺。
    
    • 1
    • 2
    • 3
    • 4

    如果是不存在的地点则返回nil,如下测试:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    redis> GEODIST Sicily Palermo Catania
    "166274.1516"
    redis> GEODIST Sicily Palermo Catania km
    "166.2742"
    redis> GEODIST Sicily Palermo Catania mi
    "103.3182"
    redis> GEODIST Sicily Foo Bar
    (nil)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.4:georadius

    以给定的经纬度为中心,并指定距离范围,返回该距离范围内的所有地点信息,格式如下:

    GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    
    • 1

    参数说明如下:

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

    测试如下:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    redis> GEORADIUS Sicily 15 37 200 km WITHDIST
    1) 1) "Palermo"
       2) "190.4424"
    2) 1) "Catania"
       2) "56.4413"
    redis> GEORADIUS Sicily 15 37 200 km WITHCOORD
    1) 1) "Palermo"
       2) 1) "13.36138933897018433"
          2) "38.11555639549629859"
    2) 1) "Catania"
       2) 1) "15.08726745843887329"
          2) "37.50266842333162032"
    redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
    1) 1) "Palermo"
       2) "190.4424"
       3) 1) "13.36138933897018433"
          2) "38.11555639549629859"
    2) 1) "Catania"
       2) "56.4413"
       3) 1) "15.08726745843887329"
          2) "37.50266842333162032"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.5:geohash

    geohash算法是一种将经纬度转换为一个整数值的算法,转换后数值接近的则说明地理位置上更接近,则当我们要按照距离来获取地点时就可以使用了。当使用redis的geohash命令获取经纬度对应的geohash值后,就可以将其作为score存储到底层的zset中了,语法格式为GEOHASH key member [member ...],测试如下:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    redis> GEOHASH Sicily Palermo Catania
    1) "sqc8b49rny0"
    2) "sqdtr74hyu0"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2:原理

    我们知道,经度的范围是[-180,180],纬度的范围是[-90,90],我们可以将其想想为如下的一个矩形,矩形中的每个点都有一个经纬度的信息,如下图:

    在这里插入图片描述

    但是这个图里每一个点目前都是用经度和纬度来表示的,如果是我们能够将矩形划分成很多个区域,然后每个区域使用一个整数来表示,则就很容易方便查找周边经纬度了,而redis就使用GeoHash编码方法实现了这个需求,基本原理是二分区间,区间编码,二分区间的意思是将经度或纬度的范围平分为两份,区间编码就是对每一份给一个编码,具体的,redis会分别对经度和纬度执行GeoHash,然后将二者的编码合并为最终的编码。

    GeoHash会将经度和纬度划分为N位的二进制编码,其中N就是需要对区间划分的次数,这里我们假设N为5来编码,假设要编码的经纬度是(116.37,39.86)看下这个过程:

    首先对经度编码,如果落在左区间则编码为0,如果落在右区间则编码为1:

    二分[-180,180]为[-180,0),[0,180]左右两个区间,116.37落在右区间,所以编码为1
    二分[0,180]为[0,90),[90,180]左右两个区间,116.37落在右区间,所以编码为1
    二分[90,180]为[90,135),[135,180]左右两个区间,116.37落在左区间,所以编码为0
    二分[90,135]为[90,112.5),[112.5,135]左右两个区间,116.37落在右区间,所以编码为1
    二分[112.5,135]为[112.5,123.75),[123.75,135]左右两个区间,116.37落在左区间,所以编码为0
    
    • 1
    • 2
    • 3
    • 4
    • 5

    所以最终的编码是11010,该过程也可以用下图表示:

    在这里插入图片描述
    纬度同该编码过程,最终结果是10111,该过程可以用下图表:
    在这里插入图片描述
    最终二者组合的规则是:从左到右偶数位依次取经度的编码,奇数位依次取纬度的编码,因此我们先来取经度的编码,为1?1?0?1?0?,接着我们将其中的?,即奇数位使用纬度的编码替换,结果就是1110011101,整个过程如下图:
    在这里插入图片描述

    这样,一个经纬度就会被转换为一个整数,而相近的整数在实际的地理位置上也是更接近的,这里我们生成编码的N如果是越大,则矩形的区域也会被划分的越细,距离的匹配也会更加精确。

    其实GEO底层使用的数据结构正是zset,而DeoHash的结果是作为其score的,这样当我们查找某地附近的地点时,就可以直接上前向后直接查找了。

    3:打车场景

    假定用户打车场景中根据用户所在定位查找车辆,先模拟库中的车辆信息:

    127.0.0.1:6379> GEOADD car:location 13.361389 38.115556 "张师傅" 15.087269 37.502669 "李师傅" 17.087269 35.502669 "刘师傅" 117.087269 65.502669 "孙小姐"
    
    • 1

    现在顾客芒果小朋友要打车,假设经纬度是13.361399 38.115565,则查找其5公里范围内的车辆,如下:

    127.0.0.1:6379> GEORADIUS car:location 13.361399 38.115565 5 km
    张师傅
    
    • 1
    • 2

    可以看到只有张师傅符合要求,那么就可以给张师傅派单了。

    写在后面

    参考文章列表:

    Redis GEO

  • 相关阅读:
    Qt 渗透测试 | 【Goby】自动化漏洞扫描工具介绍、下载、使用、功能
    自学Python 59 Socket套接字编程 (二)使用socket建立 TCP和UDP“客户端/服务器”连接
    小学期,第三场-下午:WEB_sessionlfi
    【NLP】第15章 从 NLP 到与任务无关的 Transformer 模型
    【第四篇】-深入学习Git 工作区、暂存区和版本库
    使用定时器获取转速信息(PWM频率)
    react使用 Ant ui框架
    google voice获取、转移教程(2022)
    C语言指针操作(一)指针变量
    请求本地的 JSON 文件作为 mock 数据
  • 原文地址:https://blog.csdn.net/wang0907/article/details/127675491