霜天部落 | 专注PHP研发,研究LAMP高性能架构部署与优化

Redis 3.2 GEO功能支持

Redis GEO概述

Redis3.2发布RC版本已经有一段时间了,估计RedisConf 2016左右,3.2版本就能release。3.2版本中增加的最大功能就是对GEO(地理位置)的支持。说起Redis的GEO特性,最大的贡献还是咱们中国人。Redis作者在对3.2引进新特性的博客中介绍了为什么支持GEO。GEO hashing的api是在Ardb实现的,Ardb是GitHUB用户yinqiwen实现的基于Redis协议实现的NOSQL系统,Ardb支持除了Redis、还有LevelDB、RocksDB 、LMDB等KV引擎。其中Ardb实现了GEO hashing功能。从Ardb作者的用户名和标识的位置在深圳可以看出Ardb作者应该是咱中国人。Ardb是用c++写的。Redis另一个开发者Matt Stancliff从Ardb提取GEO库,用C语言改写,整合进Redis的一个自己的分支,并被Redis作者接受,合并进了3.2版本。GEO目前提供以下6个命令:

1、geoadd:增加某个地理位置的坐标;

2、geopos:获取某个地理位置的坐标;

3、geodist:获取两个地理位置的距离;

4、georadius:根据给定地理位置坐标获取指定范围内的地理位置集合;

5、georadiusbymember:根据给定地理位置获取指定范围内的地理位置集合;

6、geohash:获取某个地理位置的geohash值。

地理位置的坐标是以WGS84为标准,WGS84,全称World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统。

 

来看看LBS服务中使用最为频繁的“当坐标周围附近的点的坐标”,比如陌陌、大众点评这里app的基础功能。在Redis 3.2中可以使用函数georadiusbymember:

georadiusbymember可以根据给定地理位置获取指定范围内的地理位置集合。georadius命令传递的是坐标,georadiusbymember传递的是地理位置。georadius更为灵活,可以获取任何坐标点范围内的地理位置。但是大多数时候,只是想获取某个地理位置附近的其他地理位置,使用georadiusbymember则更为方便。georadiusbymember命令格式为(命令可选参数与georadius含义一样):

GEORADIUSBYMEMBER key member radius [m|km|ft|mi][WITHCOORD][WITHDIST][ASC|DESC][WITHHASH][COUNT count]

具体含义见:

  • m|km|ft|mi:米|公里|英里|英尺
  • WITHCOORD:传入WITHCOORD参数,则返回结果会带上匹配位置的经纬度。
  • WITHDIST:传入WITHDIST参数,则返回结果会带上匹配位置与给定地理位置的距离。
  • ASC|DESC:默认结果是未排序的,传入ASC为从近到远排序,传入DESC为从远到近排序。
  • WITHHASH:传入WITHHASH参数,则返回结果会带上匹配位置的hash值。
  • COUNT count:传入COUNT参数,可以返回指定数量的结果。

Redis GEO实现

Redis GEO实现主要包含了以下两项技术:

  • 使用geohash保存地理位置的坐标。
  • 使用有序集合(zset)保存地理位置的集合。

geohash的思想是将二维的经纬度转换成一维的字符串,geohash有以下三个特点:

  1. 字符串越长,表示的范围越精确。编码长度为8时,精度在19米左右,而当编码长度为9时,精度在2米左右;
  2. 字符串相似的表示距离相近,利用字符串的前缀匹配,可以查询附近的地理位置。这样就实现了快速查询某个坐标附近的地理位置;
  3. Geohash计算的字符串,可以反向解码出原来的经纬度;

Redis关于geohash使用了Ardb的geohash库geohash-int,Redis使用的geohash编码长度为26位。可以精确到0.59m的精度。

细心的读者可能发现,Redis没有实现地理位置的删除命令。不过由于GEO数据保存在zset中,可以用zrem来删除某个地理位置。