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



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

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

获取自己的位置

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

用关系型数据库(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为当前查看附近的用户

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

微信附近的人,用redis也能实现?(GEO)的更多相关文章

  1. 微信"附近的人"新增商家公众号入驻功能

    微信近日升级了“附近的人”,新增商家公众号(认证的服务号和有卡券功能的公众号)可自入驻,这是微信在推出卡券和微信wifi功能后,又一加强连接线下商户能力的功能. 微信在“附近的人”中 增加搜索商户功能 ...

  2. 【LBS】基于地理位置的搜索之微信 附近的人 简单实现

    缘由 本周技术群有一个同学说我们该怎么实现 由近到远的基于地理位置的搜索,我创业做电商的系统做过类似这样的服务,我把我们以前的操作给大家分享下 什么是LBS LBS 全称是 Location  Bas ...

  3. Python如何实现微信群万人同步直播?

    很多人传言微信网页版(https://wx.qq.com/)接口已经被封了,所以所有的微信都不能登录网页版,这是错误的. 2019年7月微信对网页版微信进行了动态安全策略调整,导致一大批微信号不能登录 ...

  4. 我用 Python 找出了删除我微信的所有人并将他们自动化删除了

    1. 概述 不知你是否遇到过在微信上给通讯录中的某个人发消息,结果出现了这一幕: 平时一直认为自己的心里素质过硬,不过遇到这种情况 ... 在我缓了半个钟头(半分钟)之后,缓缓拿出了手机,打开微信,找 ...

  5. 微信小程序+laravel 7+ Redis +短信宝 实现手机号验证码登录

    以下代码可以进行优化和封装:这里我实现功能为主,就不封装啦.小伙伴可以自己试着封装一下. 1:书写登录表单 <view class="container"> <v ...

  6. redis 发布订阅、geo、bitmap、hyperloglog

    1.发布订阅 简介 发布订阅类似于广播功能.redis发布订阅包括 发布者.订阅者.Channel 命令 命令 作用 时间复杂度 subscribe channel 订阅一个频道 O(n) unsub ...

  7. Redis 实战篇:GEO助我邂逅附近女神

    码老湿,阅读了你的巧用数据类型实现亿级数据统计之后,我学会了如何游刃有余的使用不同的数据类型(String.Hash.List.Set.Sorted Set.HyperLogLog.Bitmap)去解 ...

  8. Redis系列9:Geo 类型赋能亿级地图位置计算

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  9. uni-app使用wx-canvas实现微信小程序上显示地图map和坐标geo

    源码 <template> <view class="echart-box"> <canvas class="ec-canvas" ...

随机推荐

  1. 飞塔5.4和5.6版本IPSec互备冗余测试

    主电信.备联通:测试方法:修改诚盈的IPSec,将阶段一电信的对端地址改为错误的. 方法一: 通过静态路由的管理距离:电信设置为10:联通为15.经测试,可以实现自动切换,且电信恢复后 可以切换回电信 ...

  2. (5)U盘安装Linux系统

    1.前期准备 使用 U 盘安装 Linux 系统,需要准备以下工具: 大容量的U盘(安装 CentOS 6.x 系统,U 盘容量至少 8 G): UltraISO 工具,用来制作 U 盘启动盘.除此之 ...

  3. WPF权限控制——【2】模块、菜单、按钮

    周末没有工作,没有写博客,因为觉得休息很必要:曾听到一句话是这样说的:"你们得救在乎归回安息:你们得力在乎平静安稳".当我想到太阳没秒钟要燃烧420万吨的燃料时,想到的就是造物主的 ...

  4. 【Android初级】如何实现一个比相册更高大上的左右滑动特效(附源码)

    在Android里面,想要实现一个类似相册的左右滑动效果,我们除了可以用Gallery.HorizontalScrollView.ViewPager等控件,还可以用一个叫做 ViewFlipper 的 ...

  5. this.$nextTick( 回调函数 )的作用

    首先要明确几个概念 1.Vue的核心思想 数据驱动 和 组件化系统 2.同步和异步 在没有特殊情下,程序一般先执行同步代码,等待同步执行完之后,执行异步代码 下面进入正题,首先贴出程序片段: 在该段代 ...

  6. linux(10)linux vi/vim

    前言 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是vim编辑器. vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的 ...

  7. D - D (畅通工程再续)

    相信大家都听说一个"百岛湖"的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现.现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政 ...

  8. 【51nod1674】区间的价值 V2(算法效率--位运算合并优化+链表实现)

    题目链接:  51nod1674 题意:规定一个区间的价值为这个区间中所有数and起来的值与这个区间所有数or起来的值的乘积.现在l有一个 N 个数的序列,问所有n*(n+1)/2个区间的贡献的和对1 ...

  9. Linux系统编程【2】——编写who命令

    学到的知识点 通过实现who命令,学到了: 1.使用man命令寻找相关信息 2.基于文件编程 3.体会到c库函数与系统调用的不同 4.加深对缓冲技术的理解 who命令的作用 who命令的使用 在控制终 ...

  10. LINUX - 文件读写缓存

    遇到一个进程core掉后日志打印不出来的问题: 参考如下: [引用] 只有正常退出,才能做到flush.否则将写失败. 之后有百度了下中文资料,发现同样的结论. "fflush库函数的作用是 ...