地理空间数据的索引工作带来了一些独特的挑战,因为我们处理的是经纬度,即纬度和经度,而不是简单的单维变量。比如说,我们在迪拜想叫辆出租车,我们怎样才能快速找到最近的出租车?
一种方法可能是画一个圆圈或多边形围绕我们当前的位置,并在该区域内找出租车。虽然这种方法有效,但这需要复杂的算法和较多的资源,可能还需要像PostGIS这样的专业地理信息数据库来高效处理这些查询。然而,考虑到地球表面在一个正常人类寿命内基本不会变化,我们可以把地球表面分成更小的部分,并给每个部分分配一个编号来简化问题。
在本文中,我们将探讨如何使用GeoHash来实现这一点。通过递归地划分区域,我们可以快速缩小搜索范围。我们将展示如何在不依赖Redis的地理功能的情况下,在Redis中索引和查询出租车位置。通过迪拜的例子,我们将看到,通过简单的前缀搜索,GeoHash如何高效地定位附近的车辆,从而提高性能而不会增加复杂性。
在这篇文章中,我将用Upstash的免费Redis集群服务。在免费层中,你可以每天处理多达10,000个请求,这是个不错的起点。
_Redis 是一项功能强大的伟大技术,这些功能有助于提升产品的质量。然而,构建一个可靠的 Redis 集群需要丰富的经验和时间。本文赞助商 Upstash 由一群经验丰富的工程师团队运营,并提供行业标准的功能。了解更多详情.
比如说我们在迪拜找附近的车。一种方法是在我们周围画一个多边形,甚至是一个圆,然后找到圈内的车。尽管这种方法可行,但它需要更复杂的算法和资源,或者可能需要像PostGIS这样的数据库来支持,来高效处理这种需求。但最终,即使在这些数据库里,它们用的方法和这里提到的差不多。
利用GeoHash为地球表面的网格进行索引化。我们知道地球表面在人类的寿命之内是不会发生变化的!因此,我们可以把地球表面划分成更小的区域,并给每一部分分配一个编号。
所以,如果我们身在迪拜,我们就能确定附近的出租车都属于“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 的编码、解码以及查找相邻单元格。
现在我们已经了解了GeoHash如何高效地表示出迪拜的的士位置,让我们使用Redis来存取和查询这些位置吧。
存储车辆停靠点位置每个车厢的位置被编码成一个GeoHash字符串。我们把这些存储在Redis有序集中,以便高效地进行前缀查询。
- 关键字:
cabs:geohash
- 成员:连接GeoHash码与出租车ID(例如,
THRNW:<cab_id>
) - 分数: 使用
0
,因为我们执行的是字典序搜索,而不是基于分数的查询。
当出租车报告其位置在‘THRNW’ GeoHash 单元格时,我们就执行:
ZADD cabs:geohash 0 THRNW:<cab_id>
注:上述命令用于将车辆ID添加到特定的地理哈希集合中。
查找附近的空车当用户请求叫车时:
- 编码用户的地理位置: 生成用户的坐标对应的 GeoHash,例如
THRNW
。 - 确定前缀长度: 根据所需的搜索范围选择 GeoHash 的前缀长度。
- 执行前缀搜索: 使用
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,以实现更精准和高效的地理空间索引。
共同学习,写下你的评论
评论加载中...
作者其他优质文章