zoukankan      html  css  js  c++  java
  • 微信附近的人,用redis也能实现?(GEO)

    相信微信附近的人的功能大家都应该用过

    我可以很随意的通过我自己的定位能看到我附近的人,并且能看到那个人距离我的距离,大家有没有思考过这个是怎么实现的?

    作为一个程序猿任何问题应该都有一个思考的过程,而不是直接看结论,接下来大家一步一步的思考,直到问题解决。

    获取自己的位置

    附近的人其实就是一种位置的比对关系,所以第一步是得获取自己的位置,一般位置都是用经纬度来表示,具体经纬度的获取得依赖客户端,作为咱们后端程序员直接接收参数就可以了,所以这一步重点是用经纬度来表示各个节点的位置,对经纬度不是很了解的朋友可以复习一下中学的地理知识。

    用关系型数据库(mysql)的方式解决问题

    我们先把问题简化,假如我附近的人都是不动的,也就是说他们的位置是固定的,按照咱们传统的思路,就是把每个人的经纬度存起来,然后遍历这些经纬度,我们可以通过某种方法获取我和各个经纬度之间的距离,然后把相对于我距离在 5km 以内的用户展示出来就可以了

    具体实现如下

    把每个人的经纬度存起来,存储如下

    user_id longitude(经度) latitude(纬度)
    1 116.39 39.91
    2 121.48 31.4
    3 117.30 39.71
    ... ... ...

    遍历数据,和自己对比,获得每个人和自己的距离

    把数据库的所有记录都遍历一遍,把每一条记录的经纬度和自己的经纬度做个对比,就能获取到各个记录离自己的距离。

    如何根据两个经纬度,获取到这两个点之间的距离我在网上招了个方法,大家可以参考下

    /**
     * 求两个已知经纬度之间的距离,单位为米
     *
     * @param lng1 $ ,lng2 经度
     * @param lat1 $ ,lat2 纬度
     * @return float 距离,单位米
     * @author www.Alixixi.com
     */
    function getdistance($lng1, $lat1, $lng2, $lat2) {
        // 将角度转为狐度
        $radLat1 = deg2rad($lat1); //deg2rad()函数将角度转换为弧度
        $radLat2 = deg2rad($lat2);
        $radLng1 = deg2rad($lng1);
        $radLng2 = deg2rad($lng2);
        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;
        $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137 * 1000;
        return $s;
    }
    

    筛选出距离和自己在 5km 以内的数据就是我们想得到的结果

    把上次算出来的距离一一对比,在 5km 以内的数据就是我们需要的附近的人的数据。

    用关系型数据库(mysql)存在的问题

    其实用 mysql 的方式表面上看着是可以解决问题的,其实不然

    • 首先遍历数据就是遍历所有的数据,而且是在一个需要及时返回结果的接口中,这样做是非常不科学的,用户量非常多的话根本不现实
    • 遍历完了之后还得继续计算距离,这个数量级也是非常大的
    • 距离那些都弄完了还得再筛选一遍在附近的,又是一遍所有数据的遍历
    • 如果符合附近的人的要求是需要按照距离从近到远来排序,又得遍历计算

    上述方式如果用户量比较小其实是可以实现的,但是现在移动互联网公司一般用户体量都很大,全表遍历的方式基本都可以 pass 掉,所以接下来我们来看一种新的方案,用 redis geo 的方式来实现

    redis geo 介绍

    首先我们需要注意的是,redis geo 是 3.2 版本才有的,所以需要用这个功能的朋友记得更新 redis 的版本

    其实 redis geo 只有 6 个操作命令,知道这些命令基本思路就出来了

    1. GEOADD:增加某个地理位置的坐标
    2. GEOPOS:获取某个地理位置的坐标
    3. GEODIST:获取两个地理位置的距离
    4. GEORADIUS:根据给定地理位置坐标获取指定范围内的地理位置集合
    5. GEORADIUSBYMEMBER:根据给定地理位置获取指定范围内的地理位置集合
    6. GEOHASH:获取某个地理位置的 geohash 值

    对于上面的命令,我们直接看例子吧,方便大家更深入的理解

    redis> GEOADD nearbyPeople 13.36 38.11 "user_1" 15.08 37.50 "user_2"
    (integer) 2
    

    对于上面例子来说 相当于 nearbyPeople 是一个总的 key,user_1 和 user_2 是相当于 nearbyPeople 里面的两个元素以及他们对应的经纬度
    其实上述例子就是说把 user_1 和 user_2 的经纬度存在了 nearbyPeople 这个 key 中

    redis> GEOPOS nearbyPeople user_1 user_2
    1) 1) "13.36138933897018433"
       2) "38.11555639549629859"
    2) 1) "15.08726745843887329"
       2) "37.50266842333162032"
    

    这个就比较简单了,就是获取 nearbyPeople 中的元素 user_1 和 user_2 这两个元素的经纬度,当然如果之前没有 geoadd 相对应元素的经纬度的话,会返回 nil

    redis> GEODIST nearbyPeople user_1 user_2
    "166274.1516"
    redis> GEODIST nearbyPeople user_1 user_2 km
    "166.2742"
    redis> GEODIST nearbyPeople user_1 user_2 mi
    "103.3182"
    

    获取 nearbyPeople 中 user_1 和 user_2 这两个节点之间的距离,距离单位可以指定,如下所示

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

    GEORADIUS 这个比较重要,也是比较核心的一个方法,参数也比较多,咱们来具体参照文档说一说

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

    参数说明:

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

    上述命令也就是说把 nearbyPeople 中的 距离经纬度(15,37)200km 以内的元素都找出来,而且带上距离

    GEORADIUSBYMEMBER 其实和 GEORADIUS 作用都一样,唯一的区别在于

    GEORADIUS 是以某个经纬度为基准点

    GEORADIUSBYMEMBER 是以某个元素为基准点

    用 redis geo 的方式解决问题

    其实上述命令熟悉了的同学这个问题就很好解决了

    首先我们可以在后台把每个人的位置定时刷新到以 nearbyPeople 为 key 的 geo 对象中。

    reids> GEOADD nearbyPeople 13.36 38.11 "user_1" 15.08 37.50 "user_2" .......
    

    因为查看附近的人的位置信息也在 nearBy 中,所以显然用 GEORADIUSBYMEMBER 比较合适

    GEORADIUSBYMEMBER nearbyPeople user_n 5 km WITHDIST //user_n为当前查看附近的用户
    

    这样就可以完美解决我们的问题了。

  • 相关阅读:
    JQuery批量图片上传插件—Uploadify使用记录
    SQL2008 附加数据库提示 5120错误
    enter the full pathname for java.exe
    共享文件夹设置
    曾经运行该线程的应用程序域已卸载
    无法打开物理文件 。操作系统错误 5:"5(拒绝访问。)"。 消息 5120,级别 16,状态 101
    开篇一个码农,一个苦逼的码农生活经历
    POI 3.17 读取Excel(模板)、导出Excel
    Java微信支付开发之关闭订单
    Linux下安装 MySQL5.7.28
  • 原文地址:https://www.cnblogs.com/xuehao/p/14212703.html
Copyright © 2011-2022 走看看