Redis之GeoHash

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis之GeoHash

业务场景

目前很多APP例如饿了么、美团、微信,都会有附近的店、附近的人等功能,这些功能需要基于地理位置算法,根据当前用户的经纬度定位,找到指定范围内的人或店。

GeoHash是业界比较常用的地理位置排序算法,Redis也采用该算法,Geo算法将二维的经纬度数据映射到一维的整数,这样所有的元素都会被挂在到一条线上,距离相近的二维坐标映射到一维后的点之间距离也会很近,

当需要查找附近的人或店的时候,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就可以了。

二刀法

Geo算法将整个地球看成一个二维平面,然后划分成了一系列正方形的方格,就像围棋棋盘,所有地图元素坐标都被放置于唯一的方格中,方格越小,坐标越精确,随后对这些方格进行整数编码,越是靠近的方格编码越接近,

如下图,一个正方形,两刀下去切成四个小正方形,然后分别用00、01、10、11标记着四个小正方形,然后继续对每个小正方形继续进行切割,切出来的小小正方形用4个bit二进制整数表示,随着继续切下去,二进制的整数越来越长,精确度也会越来越高。

image.png

编码之后,每个地图元素的坐标都将变成一个整数,通过这个整数就可以还原元素的坐标,整数越长还原出来的坐标值的损失程度就越小,对于附近的事物这个功能而言,损失的一点精确度可以忽略不计。

上面使用的是二刀法,真实算法中还有其他的刀法,不同的算法最终编码出来的整数数字也不一样。

GeoHash会继续对得到的整数做一次base32的编码(0-9、a-z,去掉a、i、l、o四个字母)变成一个字符串,在redis中,经纬度使用52位的整数进行编码,存在zset中,value是元素的key,score是GeoHash的52位整数值,zset的score虽然是浮点数,但是对于52位的整数值,可以无损存储。

在使用redis进行Geo查询时,记住它的内部结构实际只是一个zset(skiplist),通过zset的score排序就可以得到坐标附近的其他元素,通过将score还原成坐标值就可以得到元素的原始坐标。

Geo指令

redis的Geo指令共7个。

增加 / geoadd

geoadd指令携带集合名称以及多个经纬度名称三元组,注意可以是多个三元组,如下指令,我们一共添加了五个公司的经纬度。

127.0.0.1:6379> geoadd company 116.48105 39.996794 juejin

(integer) 1


127.0.0.1:6379> geoadd company 116.514203 39.905409 ireader

(integer) 1

127.0.0.1:6379> geoadd company 116.489033 40.007669 meituan

(integer) 1

127.0.0.1:6379> geoadd company 116.562108 39.787602 jd 116.334255 40.027400 xiaomi

(integer) 2

距离 / geodist

geodist指令可以用来计算两个元素之间的距离,携带集合名称、两个单位名称和距离单位(距离单位可以是 m、km、ml 和 ft),分别代表米、千米、英里和尺。

127.0.0.1:6379> geodist company juejin ireader km

"10.5501"

127.0.0.1:6379> geodist company juejin meituan km

"1.3878"

127.0.0.1:6379> geodist company juejin meituan m

"1387.8166"

获取元素位置 / geopos

geopos指令可以获取集合中任意元素的经纬度坐标,可以一次获取多个。

geoadd进去的经纬度和geopos出来的坐标有少许误差,原因是GeoHash对二维坐标进行的一维映射是有损的,

通过映射再还原回来的值,会有较小的误差,对于附近的事物这种功能来说,这点误差是可以接受的。

127.0.0.1:6379> geopos company juejin

1) 1) "116.48104995489120483"

   2) "39.99679348858259686"

127.0.0.1:6379> geopos company meituan

1) 1) "116.48903220891952515"

   2) "40.00766997707732031"

127.0.0.1:6379> geopos company meituan juejin

1) 1) "116.48903220891952515"

   2) "40.00766997707732031"

2) 1) "116.48104995489120483"

   2) "39.99679348858259686"

获取元素hash值 / geohash

GeoHash可以获取元素的经纬度编码字符串,可以通过这个编码去 http://geohash.org/${hash} 上直接进行定位。

127.0.0.1:6379> geohash company juejin

1) "wx4gd94yjn0"

127.0.0.1:6379> geohash company meituan

1) "wx4gdg0tx40"

image.png

附近的公司 / georadiusbymember

georadiusbymember用于查询指定元素附近的其他元素。


# 范围20公里以内最多3个元素按距离正排,该命令的结果不会排除自身

127.0.0.1:6379> georadiusbymember company juejin 20 km count 3 asc

1) "juejin"

2) "meituan"

3) "ireader

# 范围20公里以内最多3个元素按距离倒排,该命令的结果排除了自身

127.0.0.1:6379> georadiusbymember company juejin 20 km count 3 desc

1) "xiaomi"

2) "ireader"

3) "meituan"

# 增加了三个可选参数 withcoord (结果包含经纬度)、withdist(返回距离)、withhash(返回hash值)

127.0.0.1:6379> georadiusbymember company juejin 20 km withcoord withdist withhash count 3 desc

1) 1) "xiaomi"

   2) "12.9606"

   3) (integer) 4069880904286516

   4) 1) "116.33425265550613403"

      2) "40.02740024658161389"

2) 1) "ireader"

   2) "10.5501"

   3) (integer) 4069886008361398

   4) 1) "116.5142020583152771"

      2) "39.90540918662494363"

3) 1) "meituan"

   2) "1.3878"

   3) (integer) 4069887179083478

   4) 1) "116.48903220891952515"

      2) "40.00766997707732031"

georadius

georadius 可以根据经纬度获取周围的其它元素,例如附近的车、餐馆、人。


# 根据定位获取周围的公司

127.0.0.1:6379> georadius company 116.514202 39.905409 20 km withdist count 3 desc

1) 1) "jd"

   2) "13.7269"

2) 1) "meituan"

   2) "11.5748"

3) 1) "juejin"

   2) "10.5501

删除

redis中Geo的存储就是使用的ZSet,所以直接通过zrem进行删除就可以了。

注意事项

由于Geo的本质就是ZSet,所以当数据量比较大的时候,百万千万级别的数据量时,在redis的集群环境中,集合可能会从一个节点迁移到另一个节点,

如果单个ZSet的数据过大,会对集群的迁移工作造成很大的影响,在集群环境中,单个key对应的数据量不宜超过1MB,否则会导致集群迁移出现卡顿现象,影响线上服务的运行,

建议Geo的数据使用单独的redis实例部署,不放在集群环境,防止以上现象。

如果数据量过亿,甚至更大,还需要对Geo数据做拆分,按国家、省、市、区拆分,降低单个ZSet集合的大小。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
消息中间件 分布式计算 NoSQL
大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
27 2
|
6月前
|
NoSQL Java 定位技术
|
NoSQL Java 定位技术
Spring Boot实战分页查询附近的人: Redis+GeoHash+Lua
Spring Boot实战分页查询附近的人: Redis+GeoHash+Lua
380 0
|
NoSQL 定位技术 Redis
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
6天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
7天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
15天前
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
90 22