阿里云Redis GEO地理位置功能上线啦

Redis 3.2版本一个重大的更新是新增了GEO地理位置相关的命令。
ApsaraDB for Redis对地理位置的支持对应的版本也已经发布了,目前可以通过提工单升级版本来支持。
目前Redis对地理位置支持提供了一下6个命令:
1. geoadd: 增加地理位置的坐标。
2. geodist: 获取两个地理位置的距离。
3. geohash: 获取地理位置的GeoHash值。
4. geopos: 获取地理位置的坐标。
5. georadius: 根据给定经纬度坐标获取指定范围内的地理位置集合。
6. georadiusbymember: 根据给定地理位置获取指定范围内的地理位置集合。

GeoHash基本原理

GeoHash是一种地址编码,通过切分地图区域为小方块(切分次数越多,精度越高),它能把二维的经纬度编码成一维的字符串。也就是说,理论上geohash字符串表示的并不是一个点,而是一个矩形区域,只要矩形区域足够小,达到所需精度即可。

编码过程

以经纬度(116.3906,39.92324)为例:
1. 对于维度39.92324, 39.92324属于(0, 90),所以取编码为1。然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。
2. 经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。
3. 接下来将经度和纬度的编码合并,奇数位是纬度,偶数位是经度,得到编码 11100 11101 00100 01111 00000 01101 01011 00001。
4. 最后,用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(39.92324, 116.3906)的编码为wx4g0ec1。

(116.3906,39.92324)对应的地图位置就是http://geohash.org/wx4g0ec1经纬度为,降低一些精度,就会是http://geohash.org/wx4g0ec,再降低一些精度,就会是http://geohash.org/wx4g0

编码特性

不难看出这样的编码方式仅用一个字符串保存经纬度信息,并且精度由字符串从头到尾的长度决定,编码长度越长,精度越高。GeoHash值的前缀相同的位数越多,代表的位置越接近,可以方便索引。(反之不成立,位置接近的GeoHash值不一定相似).
但这种方案的缺点是:从geohash的编码算法中可以看出,靠近每个方块边界两侧的点虽然十分接近,但所属的编码会完全不同。实际应用中,需要通过去搜索环绕当前方块周围的8个方块来解决该问题。
除此之外,这个方案也无法直接得到距离,需要程序协助进行后续的排序计算。

具体的可以参考一下几个文档:

Redis Geo命令实现

Redis将地理位置的52位GeoHash值作为有序集合的score,将地理位置存放在有序集合中进行保存。后续按位置搜索时,依据GeoHash的特性搜索当前方块与环绕当前方块的8个方块来搜索目标位置集合。

GEOADD

增加地理位置坐标,命令格式如下:

GEOADD key longitude latitude member [longitude latitude member ...]

Redis中接受的有效的精度范围为-180到180度,有效维度范围为-85.05112878到 85.05112878度(靠近南北极的一小块地方是无法生成索引的)。

实现方式:

Redis内部使用有序集合来保存key,每一个member的score大小为一个52位的Geohash值(double类型精度为52位)。
实际上Redis内部实现的时候就是将GEOADD命令转换成ZADD命令来实现的。(这也解释了为什么没有专门的georem命令,地理位置信息是通过使用ZREM命令来删除成员。)
GEOADD命令的实现如下

void geoaddCommand(client *c) {
    ...
    int elements = (c->argc - 2) / 3;
    int argc = 2+elements2; / ZADD key score ele ... */
    robj *argv = zcalloc(argcsizeof(robj*));
    argv[0] = createRawStringObject("zadd",4);
    argv[1] = c->argv[1]; / key /
    incrRefCount(argv[1]);

    /* Create the argument vector to call ZADD in order to add all
     * the score,value pairs to the requested zset, where score is actually
      an encoded version of lat,long. /
    int i;
    for (i = 0; i < elements; i++) {
        double xy[2];

        if (extractLongLatOrReply(c, (c->argv+2)+(i*3),xy) == C_ERR) {
            for (i = 0; i < argc; i++)
                if (argv[i]) decrRefCount(argv[i]);
            zfree(argv);
            return;
        }

        / Turn the coordinates into the score of the element. /
        GeoHashBits hash;
        geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);
        GeoHashFix52Bits bits = geohashAlign52Bits(hash);
        robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));
        robj val = c->argv[2 + i  3 + 2];
        argv[2+i*2] = score;
        argv[3+i*2] = val;
        incrRefCount(val);
    }

    / Finally call ZADD that will do the work for us. /
    replaceClientCommandVector(c,argc,argv);
    zaddCommand(c);
}

例子

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEODIST Sicily Palermo Catania
"166274.15156960039"
redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"

GEODIST

返回两点间距离,命令格式如下

GEODIST key member1 member2 [unit]

单位可选项为m(米,默认值), km(千米),mi(英里),ft(英尺)。
返回double值,若有member不存在,则返回NULL.

实现方式:

使用WGS84坐标系统,计算距离时使用Haversine公式。由于地球并不是严格标准的,计算出来的距离有最大约0.5%的误差。

例子:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEODIST Sicily Palermo Catania
"166274.15156960039"
redis> GEODIST Sicily Palermo Catania km
"166.27415156960038"
redis> GEODIST Sicily Palermo Catania mi
"103.31822459492736"
redis> GEODIST Sicily Foo Bar
(nil)

GEOHASH

返回key中对应成员的geohash值。命令格式如下:

GEOHASH key member [member ...]

实现方式:

Redis在内部生成有序集合成员score时的geohash值与标准的算法略有差异(Redis内部使用-85,85作为维度范围,标准使用-90,90)。
这个命令返回的是标准值,与https://en.wikipedia.org/wiki/Geohash中标准算法和geohash.org网站的结果一致。代码如下:

/ Get Score /
zsetScore(zobj, c->argv[j], &score);

/* The internal format we use for geocoding is a bit different
 * than the standard, since we use as initial latitude range
 * -85,85, while the normal geohashing algorithm uses -90,90.
 * So we have to decode our position and re-encode using the
  standard ranges in order to output a valid geohash string. /

/ Decode... /
double xy[2];
if (!decodeGeohash(score,xy)) {
    addReply(c,shared.nullbulk);
    continue;
}

/ Re-encode /
GeoHashRange r[2];
GeoHashBits hash;
r[0].min = -180;
r[0].max = 180;
r[1].min = -90;
r[1].max = 90;
geohashEncode(&r[0],&r[1],xy[0],xy[1],26,&hash);

例子

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"

GEOPOS

获取地理位置的经纬度坐标,命令格式如下:

GEOPOS key member [member ...]

经纬度坐标是被转成52位的GeoHash保存起来的,返回的时候重新解码成经纬度坐标。由于精度问题,返回值可能与设置的值略有差异。

例子

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOPOS Sicily Palermo Catania NonExisting
1) 1) "13.361389338970184"
   2) "38.115556395496299"
2) 1) "15.087267458438873"
   2) "37.50266842333162"
3) (nil)

GEORADIUS, GEORADIUSBYMEMBER

获取指定范围内的地理位置集合,命令格式如下:

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

GEORADIUS与GEORADIUSBYMEMBER,前一个是获取任意经纬度周围的地理集合,后一个是获取某个地理位置周围的地理位置集合。它们的内部实现和可选参数是一致的。
可选项:
WITHDIST: 同时返回地理位置与给定位置的距离
WITHCOORD: 同时返回地理位置的经纬度坐标
WITHHASH: 同时返回Redis内部的GeoHash值(非标准算法值),一般用于debug
ASC|DESC:结果按距离升降序排序
STORE|STOREDIST: 结果存到新的有序集合中,前者以GeoHash值做score,后者以与指定位置的距离作score,该选项与WITH[DIST|COORD|HASH]选项冲突

实现方式:

GeoHash值的前缀相同的位数越多,代表的位置越接近,可以方便索引。但反之不成立,位置接近的GeoHash值不一定相似。靠近每个方块边界两侧的点虽然十分接近,但所属的编码会完全不同。实际应用中,需要通过去搜索环绕当前方块周围的8个方块来解决该问题。
搜索的时候会检查挡墙方块+8个覆盖整个搜索半径的区域,不断的去除geohash的低位,直到这9个方块能覆盖搜索半径位置。再一次搜索计算每个位置的距离。

例子:

GEORADIUS:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUS Sicily 15 37 200 km WITHDIST
1) 1) "Palermo"
   2) "190.4424"
2) 1) "Catania"
   2) "56.4413"
redis> GEORADIUS Sicily 15 37 200 km WITHCOORD
1) 1) "Palermo"
   2) 1) "13.361389338970184"
      2) "38.115556395496299"
2) 1) "Catania"
   2) 1) "15.087267458438873"
      2) "37.50266842333162"
redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"
   2) "190.4424"
   3) 1) "13.361389338970184"
      2) "38.115556395496299"
2) 1) "Catania"
   2) "56.4413"
   3) 1) "15.087267458438873"
      2) "37.50266842333162"

GEORADIUSBYMEMBER:

redis> GEOADD Sicily 13.583333 37.316667 "Agrigento"
(integer) 1
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUSBYMEMBER Sicily Agrigento 100 km
1) "Agrigento"
2) "Palermo"
时间: 2025-01-30 02:22:13

阿里云Redis GEO地理位置功能上线啦的相关文章

阿里云Redis LUA脚本功能上线——轻量嵌入,极速扩展,业务轻松跨平台

    阿里云Redis云数据库,全面支持LUA脚本功能,助力企业轻松迁移自建Redis数据库的业务逻辑,实现业务的跨平台复用,快速驱动业务上云.LUA语言作为目前最流行的轻量级嵌入式脚本语言,凭借其语法简单.高效稳定.支持复杂数据结构以及自动内存管理等特点,已经在众多著名的游戏程序中大量被使用,如:愤怒的小鸟.星际争霸.魔兽世界等. 了解Redis详细配置及价格>> Redis支持LUA脚本的主要优势      LUA脚本的融合将使Redis数据库产生更多的使用场景,迸发更多新的优势: 高效

开发者论坛一周精粹(第七期):阿里云Redis加速Discuz论坛访问

第七期(2017年5月1日-2017年5月7日 ) 由于阿里云ECS服务器内存只有悲催的的1G,并且只剩下了几十MB,实在不想再糟蹋她了. 你是否还在为网站访问速度而苦恼,你是否还在恐惧全国各地而来的访客.不用慌.不用怕 阿里云Redis为你保驾护航排忧解难,在这互联网的冬天让你依靠温暖的胸膛. 不要699,不要299,一年只需要99,没错你没听错.只要99元 完美缓存领回家!99元你买了不吃亏,99元你买了也不上当.不要怀疑你的眼睛,先到先得,只要99元 全能Redis领回家,不用你接不用你送

阿里云Redis加速Discuz论坛访问

写在开始 由于阿里云ECS服务器内存只有悲催的的1G,并且只剩下了几十MB,实在不想再糟蹋她了. 你是否还在为网站访问速度而苦恼,你是否还在恐惧全国各地而来的访客.不用慌.不用怕 阿里云Redis为你保驾护航排忧解难,在这互联网的冬天让你依靠温暖的胸膛. 不要699,不要299,一年只需要99,没错你没听错.只要99元 完美缓存领回家!99元你买了不吃亏,99元你买了也不上当.不要怀疑你的眼睛,先到先得,只要99元 全能Redis领回家,不用你接不用你送,一键购买服务直达.尽管只有256MB,但

阿里云Redis助力互联网+新媒体

    互联网+时代,传统媒体正在积极尝试各种方式拥抱互联网,实践全媒体融合新模式.行业不断涌现多元化的栏目内容,探索嘉宾观众实时互动新模式,增强用户参与度,增强用户粘性,提高用户观看时长.      阿里云Redis云数据库帮助企业以数据为基础,实现高可靠.高稳定的互联网+基础数据服务平台,为多媒体化晚会.活动互动评论.摇一摇红包.咻一咻等场景提供完美解决方案.     目前,众多行业客户已经选择阿里云Redis云数据库承载核心应用.央视CCTV利用Redis承载款跨年晚会的用户互动评论及红包

解密阿里云Redis助力双十一背后的技术

双11如火如荼的结束了,阿里云Redis(ApsaraDB for Redis原KVStore)也圆满完成了双11Redis的保障工作.目前阿里云Redis提供了单机版本和集群版本的Redis. 单机版本Redis具有很高的兼容性,并且支持Lua脚本及地理位置计算. 集群版本具有大容量.高性能的特性,能够突破Redis单线程的单机性能极限. 阿里云Redis默认双机热备并提供了备份恢复支持,同时阿里云Redis源码团队持续对Redis进行优化升级,提供了强大的安全防护能力.本文将选取双11的一些

干货来袭!Redis技术盛宴——阿里云Redis交流会火热召开

    2016年5月15日,阿里云Redis用户交流会在阿里巴巴北京国家广告园办公区万松书院火热召开,来自各个行业的Redis技术爱好者齐聚一堂,共同交流探讨最前沿的Redis技术,分享最干货的Redis应用案例.我们邀请到了阿里云数据库产品线的各位大咖,新浪微博的资深专家,CCTV的数据库技术核心成员,高德的数据库技术专家等行业大牛为大家做精彩分享. 阿里云Redis发展历程及未来展望--阿里云数据库产品总监  何云飞(何导)     何导主要介绍了Redis项目的发展历程及本次会议的主要目

Redis · 最佳实践 · 阿里云Redis助力双11业务

双11如火如荼的结束了,阿里云Redis(ApsaraDB for Redis原KVStore)也圆满完成了双11Redis的保障工作.目前阿里云Redis提供了单机版本和集群版本的Redis. 单机版本Redis具有很高的兼容性,并且支持Lua脚本及地理位置计算. 集群版本具有大容量.高性能的特性,能够突破Redis单线程的单机性能极限. 阿里云Redis默认双机热备并提供了备份恢复支持,同时阿里云Redis源码团队持续对Redis进行优化升级,提供了强大的安全防护能力.本文将选取双11的一些

阿里云Redis典型场景:如何构建可扩展通用排行榜系统

摘要 本文主要介绍通用排行榜的需求功能,并介绍了基于Redis的ZSET数据结构的排序方法,另外探讨了通用排行榜的架构及用户如何通过阿里云Redis解决通用排行架构的技术问题. 背景 移动互联网时代的春风刮来了各种不同的业务场景,直播元年.短视频元年.类微博信息流.各种电商业务也在这个移动互联网快车上集中爆发.在这些业务中信息成为了各种场景的关键,而信息的价值往往隐藏在各种排行中,对于一个直播大V可能关注直播人数的排行榜,因为他希望萃取他家长处:对于一个商家可能关注商品浏览排行榜,因为他希望更好

ApsaraDB for Redis(阿里云redis) 如何导出redis数据?

问题描述 ApsaraDB for Redis(阿里云redis) 如何导出redis数据? ApsaraDB for Redis(阿里云redis) 如何导出redis数据? 需求直接把所有数据复制到我本地redis,设置主从不行,求办法