【SpringBoot DB 系列】Redis 高级特性之 GEO
GEO 用于存储地理信息,最直观的就是我们日常使用的地图 app 中,如果我想查询我所在地的周边餐饮,就可以利用 geo 中的以(x,y)
为圆心,以 n 为半径,扫描坐标在这个圈内的所有餐饮店,这个 case 借助 redis 的 geo 可以很方便的实现
I. 基本使用
1. 配置
我们使用 SpringBoot 2.2.1.RELEASE
来搭建项目环境,直接在pom.xml
中添加 redis 依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
如果我们的 redis 是默认配置,则可以不额外添加任何配置;也可以直接在application.yml
配置中,如下
spring:
redis:
host: 127.0.0.1
port: 6379
password:
2. 使用姿势
geo 有 6 个常见的命令,下面逐一进行解释说明
a. geoadd 添加
存储指定的地理空间位置,一般需要三个基本的参数,经度 + 维度 + 位置名
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
来获取
/**
* 获取某个地方的坐标
*
* @param key
* @param member
* @return
*/
public List get(String key, String... member) {
// geopos xhh_pos 武汉
List list = redisTemplate.opsForGeo().position(key, member);return list;
}
c. geodist 获取距离
计算两个位置之间的距离,比如我已经写入了武汉、北京的经纬度,这个时候希望知道他们两的距离,直接geodist
即可
/**
* 判断两个地点的距离
*
* @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 以给定的经纬度为中心, 返回与中心的距离不超过给定最大距离的所有位置元素。
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> results = redisTemplate.opsForGeo()
.radius(key, circle, args);
System.out.println(results);
}
e. georadiusbymember 获取临近元素
和上面的作用差不多,区别在于上面参数是经纬度,这里是位置
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> results = redisTemplate.opsForGeo()
.radius(key, member, distance, args);
System.out.println(results);
}
f. geohash
GeoHash 将二维的经纬度转换成字符串,将二维的经纬度转换为一维的字符串,可以方便业务优化;geohash 有自己的一套算法,这里不详细展开,有兴趣的小伙伴可以搜索一下
public void geoHash(String key) {
// geohash xhh_pos 武汉
List 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. 项目
系列博文
- 【DB 系列】Redis 高级特性之 HyperLoglog
- 【DB 系列】Redis 高级特性之 Bitmap 使用姿势及应用场景介绍
- 【DB 系列】Redis 之管道 Pipelined 使用姿势
- 【DB 系列】Redis 集群环境配置
- 【DB 系列】借助 Redis 搭建一个简单站点统计服务(应用篇)
- 【DB 系列】借助 Redis 实现排行榜功能(应用篇)
- 【DB 系列】Redis 之 ZSet 数据结构使用姿势
- 【DB 系列】Redis 之 Set 数据结构使用姿势
- 【DB 系列】Redis 之 Hash 数据结构使用姿势
- 【DB 系列】Redis 之 List 数据结构使用姿势
- 【DB 系列】Redis 之 String 数据结构的读写
- 【DB 系列】Redis 之 Jedis 配置
- 【DB 系列】Redis 之基本配置
工程源码
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 项目源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/122-redis-template
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰 Blog 个人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top