Redis地理空间(geospatial)介绍和简单使用

时间:2021-08-26 16:11:56

在Redis最新的3.2版本中加入了地理空间(geospatial)以及索引半径查询的功能,这在需要地理位置的应用上或许可以一展身手,先来看下官网对geospatial的介绍

Adds the specified geospatial items (latitude, longitude, name) to the specified key. Data is stored into the key as a sorted set, in a way that makes it possible to later retrieve items using a query by radius with theGEORADIUS or GEORADIUSBYMEMBER commands.

The command takes arguments in the standard format x,y so the longitude must be specified before the latitude. There are limits to the coordinates that can be indexed: areas very near to the poles are not indexable. The exact limits, as specified by EPSG:900913 / EPSG:3785 / OSGEO:41001 are the following:

  • Valid longitudes are from -180 to 180 degrees.
  • Valid latitudes are from -85.05112878 to 85.05112878 degrees.

The command will report an error when the user attempts to index coordinates outside the specified ranges.

Note: there is no GEODEL command because you can use ZREM in order to remove elements. The Geo index structure is just a sorted set.

把某个具体的位置信息(经度,纬度,名称)添加到指定的key中,数据将会用一个sorted set存储,以便稍后能使用 GEORADIUSGEORADIUSBYMEMBER命令来根据半径来查询位置信息。

这个命令(指GEOADD)的参数使用标准的x,y形式,所以经度(longitude)必须放在纬度(latitude)之前,对于可被索引的坐标位置是有一定限制条件的:非常靠近极点的位置是不能被索引的, 在EPSG:900913 / EPSG:3785 / OSGEO:41001指定如下:

  • 有效的经度是-180度到180度
  • 有效的纬度是-85.05112878度到85.05112878度
如果使用了超出有效范围的经纬度,此命令会返回一个错误。 提示:之所以没有GEODEL命令,是因为你可以用ZREM来删除一个元素,GEO索引结构实际上就是一个sorted set。

GEO提供了一些命令来让我们使用,简单介绍如下:

  • GEOADD 添加一个或多个地理位置元素到一个key中 
  • 格式:GEOADD key longitude latitude member [longitude latitude member ...]

  • GEODIST 返回一个key中指定两个位置之间的距离
  • 格式:GEODIST key member1 member2 [unit]  unit可以指定长度单位:m,km,ft等 默认为m

  • GEOHASH  返回一个或多个位置元素的 Geohash 表示,Geohash是一种经纬度散列算法,具体请百度。
  • 格式: GEOHASH key member [member ...]

  • GEOPOS 返回一个或多个位置的经纬度信息,由于采用了geohash算法,返回的经纬度和添加时的数据可能会有细小误差
  • 格式: GEOPOS key member [member ...]

  • GEORADIUS  以给定位置为中心,半径不超过给定半径的附近所有位置
  • 格式 GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

  • GEORADIUSBYMEMBER 和GEORADIUS相似,只是中心点不是指定经纬度,而是指定已添加的某个位置作为中心
  • 格式: GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

命令行演示(请使用Redis3.2及以上版本):
127.0.0.1:6379> GEOADD cities 116.404269 39.91582 "beijing" 121.478799 31.235456 "shanghai"
(integer) 2
127.0.0.1:6379> ZRANGE cities 0 -1 WITHSCORES
1) "shanghai"
2) "4054803475356102"
3) "beijing"
4) "4069885555377153"
127.0.0.1:6379> GEODIST cities beijing shanghai km
"1068.5677"
127.0.0.1:6379> GEOHASH cities shanghai 
1) "wtw3sq4ptp0"
127.0.0.1:6379> GEOPOS cities beijing shanghai
1) 1) "116.40426903963088989"
   2) "39.91581928642635546"
2) 1) "121.47879928350448608"
   2) "31.23545629441388627"
127.0.0.1:6379> GEOADD cities 120.165036 30.278973 hangzhou
(integer) 1
127.0.0.1:6379> GEORADIUS cities 120 30 500 km
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> GEORADIUSBYMEMBER cities shanghai 200 km
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> ZRANGE cities 0 -1 WITHSCORES
1) "hangzhou"
2) "4054134264615180"
3) "shanghai"
4) "4054803475356102"
5) "beijing"
6) "4069885555377153"
127.0.0.1:6379> ZREM cities hangzhou
(integer) 1
127.0.0.1:6379> ZRANGE cities 0 -1 WITHSCORES
1) "shanghai"
2) "4054803475356102"
3) "beijing"
4) "4069885555377153"
127.0.0.1:6379> 

PHP代码演示:

由于文档没有介绍geo相关的函数使用,也不知道redis扩展是否实现了geo的相关命令,这里我们用Redis扩展的rawCommand方法来执行原生命令。


<?php

$redis = new Redis();
$redis->connect('127.0.0.1');

$redis->del('cities');

var_dump($redis->rawCommand('geoadd', 'cities', '116.404269', '39.91582', 'beijing', '121.478799', '31.235456', 'shanghai'));

echo 'geodist: ' . $redis->rawCommand('geodist', 'cities', 'beijing', 'shanghai', 'km') . PHP_EOL;

$pos = $redis->rawCommand('geopos', 'cities', 'shanghai');

var_dump($pos);

$places = $redis->rawCommand('GEORADIUS', 'cities', '120', '31', '300', 'km');

var_dump($places);

$redis->close();


执行结果:

int(2)
geodist: 1068.5677
array(1) {
[0]=>
array(2) {
[0]=>
string(21) "121.47879928350448608"
[1]=>
string(20) "31.23545629441388627"
}
}
array(1) {
[0]=>
string(8) "shanghai"
}


That's it!