【DB系列】Redis高级特性之GEO

文章目录
  1. I. 基本使用
    1. 1. 配置
    2. 2. 使用姿势
      1. a. geoadd 添加
      2. b. geopos 获取坐标
      3. c. geodist 获取距离
      4. d. georadius 获取临近元素
      5. e. georadiusbymember 获取临近元素
      6. f. geohash
    3. 3. 小结
  2. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog

GEO用于存储地理信息,最直观的就是我们日常使用的地图app中,如果我想查询我所在地的周边餐饮,就可以利用geo中的以(x,y)为圆心,以n为半径,扫描坐标在这个圈内的所有餐饮店,这个case借助redis的geo可以很方便的实现

I. 基本使用

1. 配置

我们使用SpringBoot 2.2.1.RELEASE来搭建项目环境,直接在pom.xml中添加redis依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

如果我们的redis是默认配置,则可以不额外添加任何配置;也可以直接在application.yml配置中,如下

1
2
3
4
5
spring:
redis:
host: 127.0.0.1
port: 6379
password:

2. 使用姿势

geo有6个常见的命令,下面逐一进行解释说明

a. geoadd 添加

存储指定的地理空间位置,一般需要三个基本的参数,经度 + 维度 + 位置名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private final StringRedisTemplate redisTemplate;

public GeoBean(StringRedisTemplate stringRedisTemplate) {
this.redisTemplate = stringRedisTemplate;
}

/**
* 添加geo信息
*
* @param key 缓存key
* @param longitude 经度
* @param latitude 纬度
* @param member 位置名
*/
public void add(String key, double longitude, double latitude, String member) {
// geoadd xhh_pos 114.31 30.52 武汉 116.46 39.92 北京
redisTemplate.opsForGeo().add(key, new Point(longitude, latitude), member);
}

b. geopos 获取坐标

上面添加一组坐标 + 地理位置到redis中,如果我们想知道某个位置的坐标,则可以借助geopos来获取

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 获取某个地方的坐标
*
* @param key
* @param member
* @return
*/
public List<Point> get(String key, String... member) {
// geopos xhh_pos 武汉
List<Point> list = redisTemplate.opsForGeo().position(key, member);
return list;
}

c. geodist 获取距离

计算两个位置之间的距离,比如我已经写入了武汉、北京的经纬度,这个时候希望知道他们两的距离,直接geodist即可

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 判断两个地点的距离
*
* @param key
* @param source
* @param dest
* @return
*/
public Distance distance(String key, String source, String dest) {
// 可以指定距离单位,默认是米, ft->英尺, mi->英里
// geodist xhh_pos 武汉 北京 km
return redisTemplate.opsForGeo().distance(key, source, dest);
}

d. georadius 获取临近元素

georadius 以给定的经纬度为中心, 返回与中心的距离不超过给定最大距离的所有位置元素。

1
2
3
4
5
6
7
8
9
10
11
 public void near(String key, double longitude, double latitude) {
// georadius xhh_pos 114.31 30.52 5km
Circle circle = new Circle(longitude, latitude, 5 * Metrics.KILOMETERS.getMultiplier());
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates()
.sortAscending().limit(5);
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo()
.radius(key, circle, args);
System.out.println(results);
}

e. georadiusbymember 获取临近元素

和上面的作用差不多,区别在于上面参数是经纬度,这里是位置

1
2
3
4
5
6
7
8
9
10
11
12
public void nearByPlace(String key, String member) {
// georadiusbymember xhh_pos 武汉 1100 km
Distance distance = new Distance(5, Metrics.KILOMETERS);
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates()
.sortAscending()
.limit(5);
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo()
.radius(key, member, distance, args);
System.out.println(results);
}

f. geohash

GeoHash将二维的经纬度转换成字符串,将二维的经纬度转换为一维的字符串,可以方便业务优化;geohash有自己的一套算法,这里不详细展开,有兴趣的小伙伴可以搜索一下

1
2
3
4
5
6
public void geoHash(String key) {
// geohash xhh_pos 武汉
List<String> results = redisTemplate.opsForGeo()
.hash(key, "北京", "上海", "深圳");
System.out.println(results);
}

3. 小结

geo更适用于地图这种业务场景中,关于这块的业务没怎么接触过,也不太好确定诸如百度地图、高德地图这种是否有在真实业务中采用;如果我们把目标缩小一点,改成一个地下车库的导航,统计所在位置周边的空余车位,位置导航,停车位记录,感觉有点靠谱

注意上面的六个操作命令,没有删除,但如果我们错误的写入了一个数据,难道没法删除么?

  • 使用 zrem key member 执行删除操作,如上面的case中,删除北京的坐标,可以: zrem xhh_pos 北京

为什么可以这么操作?

  • geo的底层存储借助ZSET来实现的,因此zset的操作符都是支持的,geo添加的元素,会通过算法得出一个score,如上面case中的北京,武汉添加之后,zset值为

II. 其他

0. 项目

系列博文

工程源码

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

一灰灰blog


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到