背景介绍:目前随着电商、社交、游戏和代购等的流行,“附近的人”这一功能提供了一种便捷的方式允许同一地区或者一定距离范围内的用户进行相互交流的途径,一般都是在用户点击某个菜单或按钮时记录用户的坐标信息,拿微信的“附近的人”功能举例子,如下图所示,
当你在点击附近的人时微信服务端会提示获取你所在位置的经纬度,记录到服务端,右上角的小脚印就表示你的经纬度信息被记录。然后服务端会根据你的位置信息拉取附近同样在服务器端有位置记录的用户信息,按照距离进行排序。一般来说“附近的人”功能只要能否大体反应距你多少米或千米范围内有XX用户即可,这句话体现了两个知识点:对精度要求不高和一定范围内(具体指多少M或KM)的用户;目前“附近的人”实现方式有很多,各有利弊,本文基于Redis(v3.2+)实现,redis3.2版本起,提供了以geo为前缀的命令采用geohash用于存储地理位置坐标信息,并对储存的地理位置信息进行操作,常用命令如下:
# GEOADD 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称##(member)添加到指定的 key 中。
GEOADD key longitude latitude member [longitude latitude member ...]
# geopos 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil
GEOPOS key member [member ...]
# geodist 用于返回两个给定位置之间的距离
GEODIST key member1 member2 [m|km|ft|mi]
# georadius 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
# georadiusbymember 和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是georadiusbymember 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
# GEOHASH 用于获取一个或多个位置元素的 geohash 值。
GEOHASH key member [member ...]
简单演示下,假设有两个位置,通过地图拾取坐标系统获取到北京南站( 116.387021,39.873306)和北京西站(116.327805,39.900766),然后在地图上大体测距两个地点距离为5.9公里。
那么下面看看,如果利用redis的GEO命令计算两个站之间的距离吧,执行过程如下,可以看出利用GEODIST命令计算出来的两个位置之间的距离和地图标注的距离大致一样,要记住Redis GEO采用geohash来保存地理位置坐标,误差肯定是存在的,在实现功能时要考虑如何消减误差产生的影响:
比如我目前位于北京动物园(116.344478,39.946361),我想看看6KM范围内是否有高铁站,两种思路:要么先GEOADD添加,然后再利用GEORADIUSBYMEMBER或者利用GEORADIUS命令来实现该功能都可以:
好的,现在开始编码:
1、新建一个SpringBoot项目,并引入spring-boot-redis依赖,项目结构和pom.xml文件如下:
<