当前位置: 首页 > 后端技术 > PHP

微信附近的人也可以用redis实现?(GEO)

时间:2023-03-29 14:30:47 PHP

相信大家应该都用过微信附近的人功能吧。我可以很容易地通过自己的位置看到靠近我的人,我可以看到那个人离我的距离。你有经验吗?你有想过这是如何实现的吗?作为程序员,任何问题都应该有一个思考的过程,而不是直接看结论,然后大家一步步思考,直到问题解决。获取你所在位置附近的人其实就是一个位置比较关系,所以第一步就是获取你所在的位置。通常,位置由纬度和经度表示。具体经纬度的获取要看客户端,作为我们的后台。程序员直接接收参数即可,所以这一步的重点是用经纬度来表示每个节点的位置。对经纬度不是很了解的朋友可以复习一下中学的地理知识。使用关系型数据库(mysql)解决问题,我们先把问题简单化一下。如果我周围的人不动,也就是说他们的位置是固定的。按照我们传统的思维,就是把每个人的经纬度存起来,然后遍历这些经纬度。我们可以通过某种方法得到我和各个经纬度的距离,然后显示距离我5公里以内的用户。具体实现如下:保存每个人的经纬度,存储下面的user_idlongitude(经度)latitude(纬度)1116.3939.912121.4831.43117.3039.71...遍历数据,和自己比较,得到每个人和自己的距离,遍历数据库中的所有记录。将每条记录的经纬度与自己的经纬度进行比较,就可以得到每条记录离自己的距离。如何根据两个经纬度得到这两个点之间的距离呢?lng1$,lng2longitude*@paramlat1$,lat2latitude*@returnfloatdistanceinmeters*@authorwww.Alixixi.com*/functiongetdistance($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可以解决,但实际上第一步是遍历所有的数据,而且是在一个需要及时返回结果的接口中。这是非常不科学的。如果用户很多,遍历完再继续计算距离是不现实的。这个数量级也是一个非常大的距离。附近的人又得重新筛选一遍,又是遍历所有数据。如果满足附近人的要求,就需要按照距离从近到远排序,还要遍历计算。上面的方法如果用户量比较少的话其实是可以实现的,但是现在移动互联网公司的用户量一般都比较大,而且整个表基本上遍历的方法都可以通过,那么我们再来看一个新的方案,使用redisgeo实现redisgeo介绍首先需要注意的是redisgeo只有3.2版本才有,所以需要使用这个功能的朋友记得更新redis版本。其实redisgeo只有6条操作命令。知道了这些命令,基本思路就出来了。GEOADD:添加某个地理位置的坐标GEOPOS:获取某个地理位置的坐标该位置的距离GEORADIUS:根据给定的地理位置坐标获取指定范围内的地理位置集合GEORADIUSBYMEMBER:获取地理位置集合根据给定的地理位置在指定范围内GEOHASH:获取某个地理位置的geohash值对于上面的命令,我们只是看例子,让大家对redis有更深入的了解>GEOADDnearbyPeople13.3638.11"user_1"15.0837.50"user_2"(integer)2对于上面的例子,相当于nearbyPeople是一个通用的key,user_1和user_2相当于nearbyPeople里面的两个元素及其对应的经纬度其实上面的例子的意思是user_1和user_2的经纬度存储在键nearbyPeople中。redis>GEOPOSnearbyPeopleuser_1user_21)1)"13.36138933897018433"2)"38.11555639549629859"2)1)"258.08"2)"37.50266842333162032"这个比较简单,就是获取nearbyPeople中user_1和user_2这两个元素的经纬度。当然如果之前没有geoadd对应的元素的经纬度,就会返回nilredis>GEODISTnearbyPeopleuser_1user_2"166274.1516"redis>GEODISTnearbyPeopleuser_1user_2km"166.2742"redis>GEODISTnearbyPeopleuser_1user_2mi"103.3182"获取nearbyPeople中user_1和user_2两个节点的距离,距离单位可以指定,如下图m:meter,默认单位km:公里.mi:英里.ft:英尺.GEORADIUS这个比较重要,也是比较核心的方法,参数比较多,我们参考文档说说GEORADIUSkeylongitudelatituderadiusm|km|ft|mi[WITHCOORD][WITHDIST][WITHHASH][COUNTcount][ASC|DESC][STOREkey][STOREDISTkey]参数说明:m:米,默认单位。km:千米。mi:英里.ft:英尺.WITHDIST:在返回位置同时时间作为位置元素,还返回位置元素与中心之间的距离。WITHCOORD:还返回位置元素的经度和纬度。WITHHASH:以52位有符号整数的形式,返回location元素经过原始geohash编码后的有序集合分数。该选项主要用于低级应用或调试,在实际中用处不大。COUNT限制返回的记录数。ASC:搜索结果从近到远排序。DESC:搜索结果按最远排序。redis>GEORADIUSnearbyPeople1537200kmWITHDIST1)1)"user_1"2)"190.4424"2)1)"user_2"2)"56.4413"以上命令表示nearbyPeople中的距离在经纬度200km以内(15,37)找出所有的元素,带上距离GEORADIUSBYMEMBER其实和GEORADIUS是一样的。唯一不同的是,GEORADIUS以一定的经纬度作为参考点。GEORADIUSBYMEMBER以某个元素为参考点,用redisgeo解决问题。其实熟悉上面命令的同学可以很轻松的解决这个问题。首先,我们可以定时刷新后台每个人的位置到以nearbyPeople为key的geo对象中。reids>GEOADDnearbyPeople13.3638.11"user_1"15.0837.50"user_2"......因为查看附近人的位置信息也在nearBy,所以使用GEORADIUSBYMEMBERGEORADIUSBYMEMBERnearbyPeopleuser_n5kmWITHDIST//user_n显然更合适目前查看附近用户可以完美解决我们的问题。