我认为 Redis 最酷的功能之一——当我发现它时让我感到惊讶——是地理空间数据结构。既然我认为它很酷,我想你们也会这么认为。所以,我将与你们分享它。
Redis 的地理空间数据结构使用起来相当简单,但也有一些有趣的细节。我们将涵盖这两者。而且,因为我不写一个带有有趣主题的博客文章就无法完成,所以这次我将使用我最喜欢的数据集:大脚怪目击事件。
大脚怪数据集有很多很棒的东西。每一行都是对大脚怪目击事件的描述,包括该事件的完整文本、发生时间,甚至还有一个分类。分类是我最喜欢的部分,尽管这些描述也很有趣!
这与我们今天所做的事情无关——我分享这个只是为了好玩——但这是大脚怪目击事件的三个类别
今天,我们将只使用大脚怪目击事件的经度、纬度和报告编号。如果您想加载数据并一起玩,我创建了一个存储库,其中包含数据、转换代码以及运行说明。当然,您还需要安装 Redis。
Geo Set 是在 Redis 中使用地理空间数据的关键数据结构。它包含一个命名的地球位置集合。向此集合添加和更新成员很容易。只需使用GEOADD命令:
> GEOADD bigfoot:sightings:locations -89.15173 37.61335 report:40120
此命令要求 Redis 将名为report:40120的成员添加到 Geo Set bigfoot:sightings:locations,其经度为-89.15173,纬度为37.61335。如果该成员不存在,则会插入它。如果存在,其位置将被更新。
注意:这种类型的操作称为“upsert”——更新和插入的混合词——是 Redis 中一种流行的模式。您会反复看到的另一种模式是可变参数命令——接受可变数量参数的命令。
GEOADD是一个可变参数命令,因为您也可以更新多个成员。像这样
> GEOADD bigfoot:sightings:locations -89.15173 37.61335 report:40120
-88.55 41.33 report:12140
注意:经度和纬度以该顺序呈现在命令中。经度——然后是纬度。如果您像很多人一样,您的直觉是先输入纬度。这很容易出错。注意它。
很好。我们已经更新了伊利诺伊州南部和芝加哥西部的大脚怪目击事件。如果您在家玩,可以使用以下命令查找它们
> HGETALL bigfoot:sightings:report:40120
> HGETALL bigfoot:sightings:report:12140
您也可以从大脚怪野外研究人员组织网站上的原始来源查找它们。没有办法按报告编号查找它们,但它是链接的一部分,所以您可能会弄清楚。
无论如何,这涵盖了创建和更新。但是读取和查询呢?有几种方法可以做到这一点。如果您只想提取坐标,可以使用GEOPOS命令:
> GEOPOS bigfoot:sightings:locations report:40120
您也可以可变参数地查询成员
> GEOPOS bigfoot:sightings:locations report:40120 report:12140
这将返回坐标,其精度可能比输入时更高。这是它们存储方式的产物,我们稍后会讨论它
1) 1) "-89.15173262357711792" 2) "37.61334955747530984" 2) 1) "-88.55000048875808716" 2) "41.330001054529383"
再次注意,返回的两组坐标是先经度,然后是纬度。
您可能想要做一些更复杂的事情。也许您想确定两次大脚怪目击事件之间的距离。您可以使用GEODIST命令找到答案:
> GEODIST bigfoot:sightings:locations report:40120 report:12140
只需将一个键和两个成员交给GEODIST,它会告诉您它们之间的距离,以米为单位。如果您对米不感兴趣,而更喜欢公里或自由单位(即英尺和英里),您只需在命令末尾指定您想要的单位
> GEODIST bigfoot:sightings:locations report:40120 report:12140 m
> GEODIST bigfoot:sightings:locations report:40120 report:12140 km
> GEODIST bigfoot:sightings:locations report:40120 report:12140 ft
> GEODIST bigfoot:sightings:locations report:40120 report:12140 mi
这两次目击事件相距约 260 英里。
您还可以在特定点周围的半径内找到 Geo Set 的成员。该点可以是坐标对,也可以是Geo Set 的另一个成员。俄亥俄州东南部实际上是大脚怪目击事件的热点地区。让我们看看在雅典(该州该地区最大的城市)附近有多少目击事件:
> GEORADIUS bigfoot:sightings:locations -82.109149 39.319950 25 mi
相当多
1) "report:4982"
2) "report:9042"
...省略...
15) "report:8017"
16) "report:10945"
这是相同的命令,但使用成员而不是坐标
> GEORADIUSBYMEMBER bigfoot:sightings:locations report:9042 25 mi
您还可以通过在末尾添加WITHCOORD和/或WITHDIST来请求有关这些位置的额外信息
> GEORADIUS bigfoot:sightings:locations -82.109149 39.319950 25 mi WITHCOORD WITHDIST
1) 1) "report:4982" 2) "16.2549" 3) 1) "-82.3997005820274353" 2) "39.25109119711087402" ...snip... 16) 1) "report:10945" 2) "24.7297" 3) 1) "-81.85806065797805786" 2) "39.01971931496525059"
所以这就是GEOADD、GEOPOS、GEODIST、GEORADIUS和GEORADIUSBYMEMBER。这些命令允许您在 Redis 中创建、读取和更新地理空间数据。但是,您可能会注意到我只谈到了 CRUD 的四个方面中的三个。删除成员的 GEO-something-or-other 命令在哪里?嗯,这是第一个有趣的地方。
所以在我们谈论删除之前,我们需要谈谈地理哈希。地理哈希是一种巧妙地将坐标存储在单个整数中,然后将其表示为 base-32 编码字符串的方法。
地理哈希通过不断细分地球,一次存储一个坐标位。第一次分割沿着本初子午线将地球分为两半。如果位置在西半球,则最高有效位为 0;如果在东半球,则为 1。下一位沿着南北半球分割。北方为 1,南方为 0。您不断像这样分割,先是东西方向,然后是南北方向,并不断添加位,直到达到您满意的分辨率。然后,您对这些位进行编码。
Redis 可以使用 GEOHASH 命令为您执行此操作。它的工作方式与 GEOPOS 类似,但它返回的是地理哈希,而不是坐标。当然,它是可变参数的。
> GEOHASH bigfoot:sightings:locations report:40120 report:12140
1) "dn8tgr39wh0"
2) "dp350gzueq0"
这些是 base-32 编码的数字。这意味着地理集合可以表示为具有数值的集合。Redis 有一种这样的数据类型:有序集合。而这正是 Redis 实现地理集合的方式。
在幕后,地理集合是有序集合。有序集合中的数字是一个 64 位浮点数。代表地理哈希的整数存储在该浮点数中,并且可以安全地不大于 52 位整数(这已经足够了)。当您调用 GEOHASH 时,Redis 获取该数字并进行 base-32 编码。当您调用 GEOPOS 时,Redis 获取该数字并将其转换为坐标,这就是为什么您输入的坐标与您获得的坐标不完全相同的原因。
由于地理集合是有序集合,因此所有的有序集合命令都适用于地理集合——尽管有些命令有一些注意事项。例如,想要获取地理哈希的底层整数吗?
> ZSCORE bigfoot:sightings:locations report:40120
"1781261397121617"
想要获取所有成员吗?
> ZRANGE bigfoot:sightings:locations 0 -1
1) "report:8059"
2) "report:4886"
3) "report:1031"
...省略...
实际上,不要这样做。请改用ZSCAN:
> ZSCAN bigfoot:sightings:locations 0 COUNT 5
那么,如何删除成员呢?使用ZREM
> ZREM bigfoot:sightings:locations report:40120 report:12140
现在,当您去获取它们时,它们已经消失了
> GEOPOS bigfoot:sightings:locations report:40120 report:12140
1) (nil)
2) (nil)
这几乎是您能对地理集合做的所有事情。但这当然不是您能利用地理集合做的所有事情。地理空间数据有很多应用。除了有点异想天开的跟踪大脚怪之外,您还可以将地理空间数据与 Pub/Sub 结合起来,以实时跟踪您想要的任何事物——无论是移动应用程序的用户、车队中的卡车,还是环境研究中的非神秘动物。