文章目录
- 1. 地理坐标类型 Redis GEO
1. 地理坐标类型 Redis GEO
Redis GEO是Redis在3.2版本中新添加的特性,通过这一特性,用户可以将经纬度格式的地理坐标存储到Redis中,并对这些坐标执行距离计算、范围查找等操作。
地理坐标通常用于与地图和位置相关的程序中。
1.1 GEOADD:存储坐标
通过使用GEOADD命令,用户可以将给定的一个或多个经纬度坐标存储到位置集合中,并为这些坐标设置相应的名字。
GEOADD命令会返回新添加至位置集合的坐标数量作为返回值。
以下代码展示了如何使用GEOADD命令,将广东省的多个城市以及它们的经纬度坐标存储到一个名为GuangDong的位置集合中:
在使用GEOADD命令向位置集合添加坐标的时候,如果用户给定的位置在集合中已经有了与之相关联的坐标,那么GEOADD命令将使用用户给定的新坐标去代替已有的旧坐标。
复杂度:O(log (N)*M),其中N为位置集合目前包含的位置数量,M为用户给定的位置数量。
1.2 GEOPOS:获取指定位置的坐标
在使用GEOADD命令将位置及其坐标存储到位置集合之后,用户可以使用GEOPOS命令去获取给定位置的坐标。
GEOPOS命令会返回一个数组作为执行结果,数组中的每个项都与用户给定的位置相对应:第一个数组项记录的就是用户给定的第一个位置的坐标,而第二个数组项记录的则是用户给定的第二个位置的坐标,以此类推。数组中的每个项都包含两个元素,第一个元素是位置的经度,而第二个元素则是位置的纬度。
比如,通过执行以下命令,我们可以从位置集合GuangDong中取得清远、广州以及东莞这3个城市的坐标:
如果用户给定的位置并不存在于位置集合当中,那么GEOPOS命令将返回一个空值:
复杂度:O(log (N)*M),其中N为位置集合目前包含的位置数量,而M则为用户给定的位置数量。
1.3 GEODIST:计算两个位置之间的直线距离
在使用GEOADD命令将位置及其坐标存储到位置集合中之后,可以使用GEODIST命令计算两个给定位置之间的直线距离。
在默认情况下,GEODIST命令将以米为单位,返回两个给定位置之间的直线距离。
GEODIST命令在默认情况下将以米为单位返回两个给定位置之间的直线距离,用户也可以在有需要的情况下,通过可选的unit参数来指定自己想要使用的单位。
- m:以米为单位,为默认单位。
- km:以千米为单位。
- mi:以英里[插图]为单位。
- ft:以英尺[插图]为单位。
在调用GEODIST命令时,如果用户给定的某个位置并不存在于位置集合中,那么命令将返回空值,表示计算失败。
复杂度:O(log (N)),其中N为位置集合目前包含的位置数量。
1.4 GEORADIUS:查找指定坐标半径范围内的其他位置
通过使用GEORADIUS命令,用户可以指定一个经纬度作为中心点,并从位置集合中找出位于中心点指定半径范围内的其他位置。
- key参数用于指定执行查找操作的位置集合。
- longitude参数和latitude参数分别用于指定中心点的经度和纬度。
- radius参数用于指定查找半径。
可以指定查找半径的单位,与GEODIST命令中的unit参数一样,这个参数的值可以是m(米)、km(千米)、mi(英里)或者ft(英尺)中的任意一个。
GEORADIUS命令在返回距离时所使用的单位与进行范围查找时所使用的单位一致:如果命令在进行范围查找时使用米作为单位,那么它就以米为单位返回各个位置的距离。
向位置集合Guangdong-cities中加入清远市、广州市、深圳市、中山市、佛山市、东莞市的地理坐标。
GEOADD Guangdong-cities 113.209 23.593 Qingyuan 113.227 23.125 Guangzhou
114.053 22.555 Shenzhen 113.406 22.511 Zhongsan 113.106 23.008 Foshan 113.794
22.976 Dongguan
基于位置集合Guangdong-cities进行演示:
1.4.1 返回被匹配位置与中心点之间的距离
GEORADIUS命令具有可选的WITHDIST选项,如果用户在执行GEORADIUS命令时给定了这个选项,那么GEORADIUS命令不仅会返回位于指定半径范围内的位置,还会返回这些位置与中心点之间的距离。
我们可以通过执行以下命令,找出距肇庆市200km范围内的所有城市,并计算出这些城市与肇庆市之间的距离:
从返回的结果可以看到,中山距离肇庆约125km,深圳距离肇庆约184km,佛山距离肇庆约79km,而广州、东莞和清远则分别距离肇庆约91km、149km和107km。
1.4.2 返回被匹配位置的坐标
除了WITHDIST之外,GEORADIUS命令还提供了另一个可选项WITHCOORD,通过使用这个选项,用户可以让GEORADIUS命令在返回被匹配位置的同时,将这些位置的坐标也一并返回。
比如,通过执行以下命令,我们可以找出距离肇庆市100km范围内的所有城市,并获取这些城市的坐标:
结果显示,佛山和广州这两座城市都位于距肇庆市100km范围之内,其中佛山市的经度为113.10631066560745239,纬度为23.00883120241353907,而广州市的经度为113.22784155607223511,纬度为23.1255982020608073。
1.4.3 排序查找结果
GEORADIUS命令在默认情况下会以无序方式返回被匹配的位置,但是通过使用可选的ASC选项或DESC选项,用户可以改变这一行为,让GEORADIUS命令以有序方式返回结果。
如果用户使用了ASC选项,那么GEORADIUS将根据中心点与被匹配位置之间的距离,按照由近到远的顺序返回被匹配的位置;相反,如果用户使用的是DESC选项,那么GEORADIUS将按照由远到近的顺序返回被匹配的位置。
以下代码就展示了如何按照由近到远的顺序返回距离肇庆市150km范围内的其他城市:
命令的结果显示,在半径150km之内,距离肇庆最近的城市是佛山,之后是广州、清远和中山,而距离肇庆最远的城市则是东莞。
如果我们在使用ASC选项或者DESC选项的同时也使用WITHDIST选项,那么被匹配位置的有序性质将变得更为明显。比如,以下命令在按照由近到远的顺序返回肇庆市指定半径范围内的其他城市时,还会返回这些城市与肇庆市之间的距离:
1.4.4 限制命令获取的位置数量
默认情况下,GEORADIUS命令将返回指定半径范围内的所有其他位置,但是通过可选的COUNT选项,我们可以限制命令返回的最大位置数量。
如果我们在不使用COUNT选项的情况下,查找距离肇庆市200km范围内的城市,那么命令将返回6个城市作为结果:
但是通过使用COUNT选项,我们可以让GEORADIUS命令只返回3个城市作为结果:
1.4.5 同时使用多个可选项
用户可以通过同时使用GEORADIUS命令的多个可选项来实现更为细致和复杂的查找操作。
比如,通过同时使用WITHDIST、WITHCOORD和ASC这3个选项,我们可以在查找肇庆市指定半径范围内的其他城市时,按照由近到远的顺序对这些城市进行排序,并返回这些城市的坐标以及它们与肇庆市的距离:
或者通过同时使用ASC选项和COUNT选项,获取距离肇庆市最近的3个城市:
1.4.6 时间复杂度说明
复杂度:O(N),其中N为命令实施范围查找时检查的位置数量。
1.5 GEORADIUSBYMEMBER:查找指定位置半径范围内的其他位置
GEORADIUSBYMEMBER命令的作用和GEORADIUS命令的作用一样,都是找出中心点指定半径范围内的其他位置,这两个命令的主要区别在于GEORADIUS命令通过给定经纬度来指定中心点,而GEORADIUSBYMEMBER命令则通过选择位置集合中的一个位置作为中心点。
除了指定中心点时使用的参数不一样之外,GEORADIUSBYMEMBER命令中的其他参数和选项的意义都与GEORADIUS命令一样。
以下代码展示了如何使用Guangdong-cities位置集合中的"Guangzhou"作为中心点,查找位于它半径150km内的所有城市:
注意,GEORADIUSBYMEMBER命令在返回结果的时候,会把作为中心点的位置也一并返回。
复杂度:O(N),其中N为命令实施范围查找时检查的位置数量。
1.6 GEOHASH:获取指定位置的Geohash值
用户可以通过向GEOHASH命令传入一个或多个位置来获得这些位置对应的经纬度坐标的Geohash表示。
Geohash是一种编码格式,这种格式可以将用户给定的经度和纬度转换成单个Geohash值,也可以根据给定的Geohash值还原出被转换的经度和纬度。
当应用程序因为某些原因只能使用单个值去表示位置的经纬度时,我们就可以考虑使用GEOHASH命令去获取位置坐标的Geohash值,而不是直接使用GEOPOS命令去获取位置的经纬度。
1.6.1 在进行范围查找时获取Geohash值
GEORADIUS命令和GEORADIUSBYMEMBER命令都支持WITHHASH选项,使用了这个选项的命令将会在结果中包含被匹配位置的Geohash值。
以下代码展示了如何在查找广州市附近城市的同时,获取这些城市的Geohash值:
需要注意的是,与GEOHASH命令不一样,GEORADIUS命令和GEORADIUSBYMEMBER命令返回的是被解释为数字的Geohash值。而GEOHASH命令返回的则是被解释为字符串的Geohash值。这两个值底层的二进制位是完全相同的。
1.6.2 时间复杂度说明
复杂度:O(N),其中N为用户给定的位置数量。
1.7 使用有序集合命令操作GEO数据
有序集合可以参考本篇博客
Redis使用有序集合存储GEO数据,一个位置集合实际上就是一个有序集合:当用户调用GEO命令对位置集合进行操作时,这些命令实际上是在操作一个有序集合。
举个例子,当我们调用以下命令,将清远市的经纬度添加到Guangdong-cities位置集合时:
GEOADD Guangdong-cities 113.209 23.593 Qingyuan
Redis会把给定的经纬度转换成数字形式的Geohash值4046597930688831,然后调用ZADD命令,将位置名及其Geohash值添加到有序集合中:
ZADD Guangdong-cities 4046597930688831 Qingyuan
除了GEOADD之外,包括GEOPOS、GEODIST、GEORADIUS、GEORADIUSBYMEMBER和GEOHASH在内的所有GEO命令都是在有序集合的基础上实现的,这也使得我们可以直接使用有序集合命令对位置集合进行操作。
比如,可以使用ZRANGE命令查看位置集合存储的所有位置,以及这些位置的Geohash值:
或者使用ZCARD命令获取位置集合目前存储的位置数量:
此外,虽然Redis没有直接提供删除位置集合中指定位置的命令,但是我们可以使用ZREM命令达到相同的效果:
1.8 小结
- Redis的GEO特性允许用户将经纬度格式的地理位置存储到Redis中,并对这些位置执行距离计算、范围查找等操作。
- GEORADIUSBYMEMBER命令的作用和GEORADIUS命令的作用一样,都是找出中心点指定半径范围内的其他位置,它们之间的主要区别在于GEORADIUS命令通过给定经纬度来指定中心点,而GEORADIUSBYMEMBER命令则通过选择位置集合中的一个位置来作为中心点。
- Geohash是一种编码格式,这种格式可以将用户给定的经度和纬度转换成单个Geohash值,也可以根据给定的Geohash值还原出被转换的经度和纬度。执行GEOHASH命令即可取得给定位置的Geohash值。
- Redis使用有序集合存储GEO数据,一个位置集合实际上就是一个有序集合,因此用户也可以使用有序集合命令处理位置集合。