【附近的人】实现方案
方案一:Redis Commands: Geography Edition
自Redis 3.2开始,Redis基于geohash和有序集合提供了地理位置相关功能。Redis Geo模块包含了以下6个命令:
-
GEOADD: 将给定的位置对象(纬度、经度、名字)添加到指定的key;
-
GEOPOS: 从key里面返回所有给定位置对象的位置(经度和纬度);
-
GEODIST: 返回两个给定位置之间的距离;
-
GEOHASH: 返回一个或多个位置对象的Geohash表示;
-
GEORADIUS: 以给定的经纬度为中心,返回目标集合中与中心的距离不超过给定最大距离的所有位置对象;
-
GEORADIUSBYMEMBER: 以给定的位置对象为中心,返回与其距离不超过给定最大距离的所有位置对象。
-
GEOSEARCH: 此命令代替现在6.2已弃用的GEORADIUSand GEORADIUSBYMEMBER。
其中,组合使用GEOADD和GEORADIUS可实现“附近的人”中“增”和“查”的基本功能。要实现微信中“附近的人”功能,可直接使用GEORADIUSBYMEMBER命令。其中“给定的位置对象”即为用户本人,搜索的对象为其他用户。不过本质上,GEORADIUSBYMEMBER = GEOPOS + GEORADIUS,即先查找用户位置再通过该位置搜索附近满足位置相互距离条件的其他用户对象。
优点: 效率高,API丰富。
缺点: 维护需要对API有一定的熟悉度,数据流程的可理解性相对数据库差。如果需要查询其他业务数据需要做 in 操作。
方案二:Elasticsearch GEO
ES提供了很多地理位置的搜索方式 :
geo_bounding_box: 找出落在指定矩形框中的点。
geo_distance: 找出与指定位置在给定距离内的点。
geo_distance_range: 找出与指定点距离在给定最小距离和最大距离之间的点。
优点: 天然支持,性能很优。
缺点:需要投入时间研究一下特性,以及存储,对后续维护人员有一定要求。如果需要查询其他业务数据需要做 in 操作。
方案三:Mysql :Sql 直接进行计算
SELECT *, 6378.138 * 2 * ASIN( SQRT( POW( SIN( ( 40.0497810000 * PI() / 180 - lat * PI() / 180 ) / 2 ), 2 ) + COS(40.0497810000 * PI() / 180) * COS(lat * PI() / 180) * POW( SIN( ( 116.3424590000 * PI() / 180 - lon * PI() / 180 ) / 2 ), 2 ) ) ) AS juli FROM customer ORDER BY juli ASC
优点: 开发简单,维护容易。
缺点:不走索引,当数据量达到一定程度,需要进行优化。
方案四:Mysql :GeoHash 函数(需要升级到mysql 5.7)
优点: 函数直接调用,生成目标hash、根据hash获取经纬度。可以直接连表查询业务数据。
缺点:不支持范围查询函数,需要自行处理周边8点的问题,需要补充geo的算法。