为了账号安全,请及时绑定邮箱和手机立即绑定

如何给地球打网格:GeoHash、Redis与基于位置的服务深度解析

标签:
大数据 Redis

地理空间数据的索引工作带来了一些独特的挑战,因为我们处理的是经纬度,即纬度和经度,而不是简单的单维变量。比如说,我们在迪拜想叫辆出租车,我们怎样才能快速找到最近的出租车?

一种方法可能是画一个圆圈或多边形围绕我们当前的位置,并在该区域内找出租车。虽然这种方法有效,但这需要复杂的算法和较多的资源,可能还需要像PostGIS这样的专业地理信息数据库来高效处理这些查询。然而,考虑到地球表面在一个正常人类寿命内基本不会变化,我们可以把地球表面分成更小的部分,并给每个部分分配一个编号来简化问题。

在本文中,我们将探讨如何使用GeoHash来实现这一点。通过递归地划分区域,我们可以快速缩小搜索范围。我们将展示如何在不依赖Redis的地理功能的情况下,在Redis中索引和查询出租车位置。通过迪拜的例子,我们将看到,通过简单的前缀搜索,GeoHash如何高效地定位附近的车辆,从而提高性能而不会增加复杂性。

在这篇文章中,我将用Upstash的免费Redis集群服务。在免费层中,你可以每天处理多达10,000个请求,这是个不错的起点。

_Redis 是一项功能强大的伟大技术,这些功能有助于提升产品的质量。然而,构建一个可靠的 Redis 集群需要丰富的经验和时间。本文赞助商 Upstash 由一群经验丰富的工程师团队运营,并提供行业标准的功能。了解更多详情.

比如说我们在迪拜找附近的车。一种方法是在我们周围画一个多边形,甚至是一个圆,然后找到圈内的车。尽管这种方法可行,但它需要更复杂的算法和资源,或者可能需要像PostGIS这样的数据库来支持,来高效处理这种需求。但最终,即使在这些数据库里,它们用的方法和这里提到的差不多。

利用GeoHash为地球表面的网格进行索引化。

我们知道地球表面在人类的寿命之内是不会发生变化的!因此,我们可以把地球表面划分成更小的区域,并给每一部分分配一个编号。

https://geohash.softeng.co/

所以,如果我们身在迪拜,我们就能确定附近的出租车都属于“T”类别。如果这款应用在北美也很受欢迎,我们就可以通过这个划分跳过数百万用户。

然后,如果我们把‘T’部分划分成更小的部分,并且只关注‘TH’部分的出租车,我们就可以排除其他国家,只专注于阿联酋、卡塔尔以及邻国部分地区内的出租车。

我们再重复这个过程两次就好,过滤掉阿联酋及其邻国中不相关的区域。我们也可以对“H”部分做同样的事情。

https://geohash.softeng.co/THRN

所以现在我们知道了,如果我们想找车并且我们在朱美拉酒店区,我们只需要在“THRNW”单元格内找车。

GeoHash的一大优点是,在“THRNW”单元格中的所有出租车也都在“THRN”单元格中。因此,如果我们想要找到更多的出租车,可以从一个小单元格开始,逐步扩大搜索范围,从而覆盖更大区域并找到更多出租车。

GeoHash 是一种分层的空间数据结构,可以高效地将地理坐标编码为短的字母数字字符串。技术上,它是通过递归将地球表面分割成更小的网格,并为每个网格分配一个唯一的标识来工作的。从整个地球出发,首先根据纬度分成两部分,然后每部分再根据经度进一步划分,这一过程就这样递归地继续下去。

地理哈希库

为了高效地在我们的应用程序中实现GeoHash的编码和解码,我们可以利用各种编程语言中现有的库。这些库提供了将经纬度坐标转换为GeoHash字符串的功能,并且通常还支持查找相邻的GeoHash等其他功能。

在 Go 语言中,有多个库可以帮助我们处理 GeoHashes。一个常用的库是 [github.com/mmcloughlin/geohash](https://github.com/mmcloughlin/geohash),它支持 GeoHashes 的编码、解码以及查找相邻单元格。

使用 Redis 存储网约车位置

现在我们已经了解了GeoHash如何高效地表示出迪拜的的士位置,让我们使用Redis来存取和查询这些位置吧。

存储车辆停靠点位置

每个车厢的位置被编码成一个GeoHash字符串。我们把这些存储在Redis有序集中,以便高效地进行前缀查询。

  • 关键字:cabs:geohash
  • 成员:连接GeoHash码与出租车ID(例如,THRNW:<cab_id>
  • 分数: 使用0,因为我们执行的是字典序搜索,而不是基于分数的查询。

当出租车报告其位置在‘THRNW’ GeoHash 单元格时,我们就执行:

ZADD cabs:geohash 0 THRNW:<cab_id>

注:上述命令用于将车辆ID添加到特定的地理哈希集合中。

查找附近的空车

当用户请求叫车时:

  1. 编码用户的地理位置: 生成用户的坐标对应的 GeoHash,例如 THRNW
  2. 确定前缀长度: 根据所需的搜索范围选择 GeoHash 的前缀长度。
  3. 执行前缀搜索: 使用 ZRANGEBYLEX 查找与 GeoHash 前缀匹配的车辆,比如更多详情请参阅此处。更多详情请参阅此处。
ZRANGEBYLEX cabs:geohash [THRNW [THRNW\x

此命令检索所有GeoHash以THRNW开头的出租车,有效地查找靠近朱美拉酒店区的出租车。

性能表现和时间复杂度

  • 插入操作: 每次 ZADD 操作的时间复杂度是 O(log N),其中 N 是有序集合中的元素数量。
  • 查询: ZRANGEBYLEX 命令的时间复杂度为 O(log N + M),其中 M 是返回的元素数量。因为 M 与指定 GeoHash 单元格中的出租车数量成正比,即使在高密度区域,查询依然保持高效。
设置搜索范围

GeoHash精度与字符串长度直接挂钩:

  • 每个字符都提高精度: 每个字符都会让定位更精确。GeoHash 中的每个字符都会让定位更精确,从而提高位置的精确度。具体来说,每增加一个字符,GeoHash 所代表的区域尺寸就会缩小一半。

每个额外的GeoHash前缀字符都会通过减少代表的区域来提高位置的准确性。我们可以用更长的GeoHash前缀开始搜索,以最小化搜索区域并找到距离用户最近的出租车。如果找不到足够的出租车,我们可以缩短GeoHash前缀,逐步扩大搜索范围,根据业务逻辑进行调整。这种方法使我们能够动态地平衡精度和可用性,在搜寻出租车时更加灵活。我们还可以尝试在当前单元格及其邻居单元格中进行搜索。

等等!什么是H3?

尽管GeoHash能够将地理坐标编码为网格单元,但它因为矩形网格形状和不同纬度下网格大小的变化,导致分布不均,且在接近两极时更加不准确。为了解决这些问题,Uber开发了H3,这是一种分层的六边形地理空间索引系统。

H3将地球表面划分成六边形,这些六边形面积相等且相邻一致,提供了更加均匀的覆盖和更准确的临近计算。六边形能减少变形并避免方形网格的方向偏倚,使得H3更适合用于拼车应用中所需的各种复杂空间分析。

我们之所以有这个教程是因为,Redis 的 GEOADD 和地理空间功能是使用 GeoHash 实现的。通过理解和在 Redis 中实现地理空间索引,这让我们可以根据需求灵活定制索引系统。利用这种技术,你可以自己将 H3 集成到 Redis 中,实现更精确和高效的地理空间查询,利用六边形索引的优点。

结局

总之,我们展示了如何使用GeoHash在Redis中高效地索引和查询车辆(出租车)的位置,而不需要依赖专门的地理空间功能。通过调整GeoHash前缀长度,我们可以动态地平衡搜索精确度和性能以满足应用的需求。虽然GeoHash存在局限性,但理解其具体实现可以让我们整合更高级的系统,例如H3,以实现更精准和高效的地理空间索引。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
手记
粉丝
55
获赞与收藏
156

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消