GEO
Redis 提供了GEO地理信息定位功能,地理空间项(经度、纬度、名称),实现查找附近的人、上班打卡、自行车租赁、摇一摇等相关与地理位置信息的功能。 Redis 地理空间索引可让您存储坐标并搜索它们。 此数据结构对于查找给定半径或边界框内的附近点非常有用。
基本命令
- GEOADD将位置添加到给定的地理空间索引(请注意,使用此命令时,经度位于纬度之前)。
- GEOSEARCH返回具有给定半径或边界框的位置。
GEOADD
将指定的地理空间项(经度、纬度、名称)添加到指定的键。数据作为排序集存储到键中,这样就可以使用 GEOSEARCH 命令查询项目。
该命令采用标准格式 x,y 的参数,因此必须在纬度之前指定经度。可索引的坐标存在限制:非常靠近极点的区域不可索引。
当用户尝试索引指定范围之外的坐标时,该命令将报告错误。
通常,Redis 使用 Geohash 的变体来表示元素的位置 使用 52 位整数对位置进行编码的技术。编码是 与标准相比也不同,因为初始最小值和最大值 编码和解码过程中使用的坐标是不同的。
longitude 经度 、latitude 纬度、member 成员
GEOADD key [NX | XX] [CH] longitude latitude member [longitude
latitude member ...]
以下添加5个城市经纬度到 键 cities:geo, 如果已经存在则返回0,如果需要更新地理位置,同样使用GEOADD命令(仅管返回0)。
# 北京
192.168.88.11:6380> GEOADD cities:geo 116.28 39.54 beijing
(integer) 1
# 天津
192.168.88.11:6380> GEOADD cities:geo 117.10 39.10 tianjin
(integer) 1
# 广州
192.168.88.11:6380> GEOADD cities:geo 113.18 23.10 guangzhou
(integer) 1
# 杭州
192.168.88.11:6380> GEOADD cities:geo 120.10 30.15 hangzhou
(integer) 1
# 长沙
192.168.88.11:6380> GEOADD cities:geo 112.55 28.12 changsha
(integer) 1
注意:没有GEODEL命令,因为您可以使用ZREM 删除元素。 Geo索引结构只是一个排序集。本质是 zset 数据类型。
# 查看成员
192.168.88.11:6380> ZRANGE cities:geo 0 5
1) "guangzhou"
2) "changsha"
3) "hangzhou"
4) "beijing"
5) "tianjin"
# 查看成员带分数
192.168.88.11:6380> ZRANGE cities:geo 0 5 withscores
1) "guangzhou"
2) "4046510568184210"
3) "changsha"
4) "4050880415755396"
5) "hangzhou"
6) "4054121680734333"
7) "beijing"
8) "4069140601296155"
9) "tianjin"
10) "4069185531597821"
# 集合大小: 5
192.168.88.11:6380> ZCARD cities:geo
(integer) 5
# 删除两个元素
192.168.88.11:6380> ZREM cities:geo changsha hangzhou
(integer) 2
# 集合大小: 3
192.168.88.11:6380> ZCARD cities:geo
(integer) 3
如何计算 “beijing” 经纬度(116.28, 39.54) 的分值 4069140601296155 ? 排序集的填充方式是使用一种叫做Geohash的技术。经度位和纬度位相互交错,形成一个唯一的52位整数。
EPSG:900913 / EPSG:3785 / OSGEO:41001 指定的确切限制如下:
- 有效经度范围为 -180 到 180 度。
- 有效纬度范围为 -85.05112878 到 85.05112878 度。
按下面的编码为N=26位二进制值 ,然后把经纬度交叉组成52位二进制值即可。 (偶数位为经度、奇数位为纬度)
这里,可以通过python脚本定义两个列表,然后不断拆分区间,最后做交叉合拼两个列表。结果如下:
root@ubuntu-x64_01:/opt# python3 redis_geo_bit.py
guangzhou---> 1110011000000100011110101000111111110001100110010010
changsha ---> 1110011001000100000100011000001101010110110010000100
hangzhou ---> 1110011001110011001111000010101010000000100001111101
beijing ---> 1110011101001101110010100000000101001101010100011011
tianjin ---> 1110011101001110011100010110001000101101001111111101
再转换成十进制,就是 zset 的分数值,比如 guangzhou (1110011000000100011110101000111111110001100110010010)对应十进制 : 4046510568184210
192.168.88.11:6380> ZRANGE cities:geo 0 0 withscores
1) "guangzhou"
2) "4046510568184210"
GEOHASH
返回有效的Geohash 字符串,表示一个或多个元素在表示地理空间索引的排序集值中的位置(其中元素是使用GEOADD)。
该命令返回11个字符的Geohash字符串,因此与Redis内部52位表示相比,没有精度损失。
- 他们可以缩短,从右边删除字符。它将失去精度,但仍然指向相同的区域。
- 可以在geohash.org url中使用它们,例如http://geohash.org/<geohash-string>。
- 具有相似前缀的字符串在附近,但反之则不成立,具有不同前缀的字符串也可能在附近。
字符串越长,表示的位置更精确,例如geohash长度为8时,精度在19米左右。
下面操作返回 beijing 的 geohash 值,如下:
192.168.88.11:6380> GEOHASH cities:geo beijing
1) "wx48yn090q0"
可以在geohash.org url中使用它们: http://geohash.org/wx48yn090q0
GEOPOS
返回由排序集key表示的地理空间索引中所有指定成员的位置(经度、纬度)。当通过GEOADD填充地理空间索引时,坐标被转换为52位geohash,因此返回的坐标可能不完全是用于添加元素的坐标,但可能会引入小误差。
192.168.88.11:6380> GEOPOS cities:geo guangzhou
1) 1) "113.18000167608261108"
2) "23.10000005307264104"
192.168.88.11:6380> GEOPOS cities:geo changsha
1) 1) "112.54999905824661255"
2) "28.12000010081647616"
192.168.88.11:6380> GEOPOS cities:geo hangzhou
1) 1) "120.09999901056289673"
2) "30.14999997874437554"
192.168.88.11:6380> GEOPOS cities:geo beijing
1) 1) "116.28000229597091675"
2) "39.54000124957348561"
192.168.88.11:6380> GEOPOS cities:geo tianjin
1) 1) "117.10000187158584595"
2) "39.09999900352384117"
GEODIST
返回由排序集表示的地理空间索引中两个成员之间的距离。
给定一个表示地理空间索引的排序集,使用GEOADD命令填充,该命令返回指定单元中两个指定成员之间的距离。
如果缺少一个或两个成员,则该命令返回NULL。
单位必须为以下之一,默认为米:
- m for meters. 代表米
- km for kilometers. 代表公里
- mi for miles.代表英里
- ft for feet. 代表尺
如计算北京与天津之间的距离,并以公里为单位返回,如下:
192.168.88.11:6380> GEODIST cities:geo beijing tianjin km
"85.8689"
GEORADIUS
获取指定位置范围内的地理信息位置集合,返回使用GEOADD填充地理空间信息的已排序集合的成员,这些成员位于用中心位置和到中心的最大距离(半径)指定的区域的边界内。
该命令的常见用例是检索指定点附近的地理空间项目,距离不超过给定的米(或其他单位)。例如,这允许向应用程序附近的移动用户提供建议。
单位必须为以下之一,默认为米:
- m for meters. 代表米
- km for kilometers. 代表公里
- mi for miles.代表英里
- ft for feet. 代表尺:
GEORADIUS key longitude latitude radius <M | KM | FT | MI>
[WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]
[STORE key | STOREDIST key]
该命令可以使用以下选项返回附加信息:
- WITHDIST: 返回结果中包含到指定中心的距离。返回的距离单位与命令的radius参数指定的单位相同。
- WITHCOORD: 返回结果中包含经度、纬度坐标。
- WITHHASH: 以52位无符号整数的形式返回该项的原始geohash编码的排序集分数。
该命令默认返回未排序的项。可以使用以下两个选项调用两种不同的排序方法:
- ASC: 对返回的项目进行排序,从最近到最远,相对于中心。
- DESC: 相对于中心,从最远到最近对返回项进行排序。
默认情况下,返回所有匹配项。通过使用COUNT < COUNT >选项,可以将结果限制为前N个匹配项。
- 当提供ANY时,只要找到足够的匹配项,命令就会返回,因此结果可能不是最接近指定点的结果,但另一方面,服务器投入的精力大大降低了。
- 当没有提供ANY时,该命令将执行与指定区域匹配的项数成比例的工作,并对它们进行排序,因此使用非常小的COUNT选项查询非常大的区域可能很慢,即使只返回几个结果。
默认情况下,该命令将条目返回给客户端。可以使用以下选项之一来存储结果:
- STORE:将项目存储在使用其地理空间信息填充的已排序集合中。
- STOREDIST:将项目存储在一个排序的集合中,该集合以与中心的距离作为浮点数填充,在半径中指定的相同单位中。
如,计算距离 北京 200公里 以内的城市:
192.168.88.11:6380> GEORADIUS cities:geo 116.28 39.54 200 km
1) "beijing"
2) "tianjin"
192.168.88.11:6380> GEORADIUS cities:geo 116.28 39.54 200 km WITHCOORD WITHDIST WITHHASH
1) 1) "beijing"
2) "0.0002"
3) (integer) 4069140601296155
4) 1) "116.28000229597091675"
2) "39.54000124957348561"
2) 1) "tianjin"
2) "85.8690"
3) (integer) 4069185531597821
4) 1) "117.10000187158584595"
2) "39.09999900352384117"
192.168.88.11:6380> GEORADIUS cities:geo 116.28 39.54 200 km WITHCOORD WITHDIST WITHHASH COUNT 1 DESC
1) 1) "tianjin"
2) "85.8690"
3) (integer) 4069185531597821
4) 1) "117.10000187158584595"
2) "39.09999900352384117"
# 将项目存储在使用其地理空间信息填充的已排序集合中。
192.168.88.11:6380> GEORADIUS cities:geo 116.28 39.54 200 km COUNT 1 DESC STORE cities:georadius
(integer) 1
192.168.88.11:6380> type cities:georadius
zset
192.168.88.11:6380> ZRANGE cities:georadius 0 5 withscores
1) "tianjin"
2) "4069185531597821"
小结
- 没有GEODEL命令,因为您可以使用它ZREM来删除元素。Geo索引结构只是一个排序集。GEO没有提供删除成员的命令,因为GEO的底层实现是zset,如果要删除成员,请使用 zrem 命令来对地理位置信息进行删除。
- 当通过GEOADD填充地理空间索引时,坐标被转换为52位geohash,因此返回的坐标可能不完全是用于添加元素的坐标,即可能会引入小误差。
- 填充排序集的方式是使用一种称为 Geohash的技术。纬度和经度位交错形成唯一的 52 位整数。