geohash是关于划分经纬度的,它跟R树是挺不一样的。
R树是分散的,多维的,由底下的点聚集成树索引,geohash是把经纬度这两维度压缩到一个维度上。
那是如何将2维压缩到1维的呢?
例如,一个点(x,y)在一个象限内,x轴上正为1,负为0。y轴也是,正为1,负为0。这样,就将一个平面划分成4个方位,可以用11,10,00,01这样的二进制一维数来表示点所处的二维方位。
那这个算法是如何进行的呢。
众所周知,经度是-180到180,维度是-90到90,我们设定0为负,1为正。
假如一个点的值为(-100,30)
我们需要对经度跟维度分别计算其方位的2进制。
(-180,0)为0,(0,180)为1
因为值为-100,所以第一个值为0。
接下去继续分
(-180,-90)为0,(-90,0)为1,因为值为-100,则第二个值还是为0。
这样可以无限二分法下去。
具体几位,由需要的精度确定。
维度也同理,计算出一串二进制串。
这两条串长度相同。需要将它们合并起来,那该如何合并呢。
经度跟维度相间轮流合并,经度占偶数位(起始0位),维度占奇数位。
这样就得到了一个新的二进制。
会对这二进制做一次base32的编码(就是5位为一组)
redis里的geohash呢?
在redis里,geohash是用52位整数进行编码,放进一个zset的结构里,排序集合。
但是实际上不是这么简单,不是说在一个格子里,两个点就是最近的,如果A点在边界,B在一个格子里的对面的边界,那AB两个点的距离还不如另外的格子里的点近呢。
而且geohash算法还会有曲线跃迁,比如第二象限跟第四象限,虽然差1,但是他们距离还是挺远的。
这两点都需要考量。
使用
不过我们使用的话,就不用想那么多了。
geoadd company 116.48105 39.996794 juejin
//命令 名称 经度 维度 内部名称
因为内部本身就是一个zset,则其他的命令跟zset的命令差不多。这边就不赘述了。
geodis company juejin meituan km
//查询两个元素直接的距离
最重要的
还是查询一个点附近的点,这个功能。
georadius company 116.514202 39.905409 20 km withdist count 3 asc
查找company集合内,(116.514202 ,39.905409)20km内的,正序排名前3的元素
最后的提示
因为geohash是存在zset结构内的,其实不太会被频繁的变动,一个地图的各种数据,可能有几千万条,就不太合适布置在集群里,因为迁移会很慢。
建议是使用redis单独的实例部署。