点赞在看,养成习惯。
点赞收藏,人生辉煌。
点击关注【微信搜索公众号:编程背锅侠】,防止迷路。
业界比较通用的地理位置距离排序算法是 GeoHash 算法,Redis 也使用 GeoHash 算法。比如,我们的外卖员送快递的时候需要定位要配送的地点的距离。比如我们找工作的时候,需要查一下面试的公司的位置,以及距离有多远。以及我们在查询附近的共享单车♀️最近的有几公里。
方法 | 概述 |
---|---|
Long add(K key, Point point, M member); | 添加元素 |
List hash(K key, M… members); | 获取地理位置的哈希值 |
List position(K key, M… members); | 根据key和元素获取地理位置数据 |
Distance distance(K key, M member1, M member1, Metric metric); | 计算两个元素之间的距离 |
GeoResults |
查询指定元素附近的其它元素 |
GeoResults |
查询指定元素附近的其它元素-重载方法 |
127.0.0.1:6379> geoadd location 116.307629 40.058359 baidu
(integer) 1
127.0.0.1:6379> geohash location baidu
1) "wx4eysksu50"
127.0.0.1:6379> geopos location baidu
1) 1) "116.3076290488243103"
2) "40.05835933082178002"
127.0.0.1:6379> GEODIST location baidu meituan km
"16.4455"
# withdist 它可以用来显示距离
# asc 排序
# count 3 打印几条数据
127.0.0.1:6379> GEORADIUSBYMEMBER location baidu 20 km withcoord withdist withhash count 3 asc
1) 1) "baidu"
2) "0.0000"
3) (integer) 4069883733203026
4) 1) "116.3076290488243103"
2) "40.05835933082178002"
2) 1) "xiaomi"
2) "4.1227"
3) (integer) 4069880904286516
4) 1) "116.33425265550613403"
2) "40.02740024658161389"
3) 1) "juejin"
2) "16.2804"
3) (integer) 4069887154388167
4) 1) "116.48104995489120483"
2) "39.99679348858259686"
// 根据指定key将指定元素添加到redis中。key:redis的key,point:封装经纬度的对象,member:元素
@Nullable
Long add(K key, Point point, M member);
/**
* 地理位置的初始化数据
*/
@Test
public void test_user_simple_geohash_init(){
String key = "location";
stringRedisTemplate.opsForGeo().add(key, new Point(116.48105,39.996794), "juejin");
stringRedisTemplate.opsForGeo().add(key, new Point(116.514203, 39.905409), "ireader");
stringRedisTemplate.opsForGeo().add(key, new Point(116.489033, 40.007669), "meituan");
stringRedisTemplate.opsForGeo().add(key, new Point(116.562108, 39.787602), "jd");
stringRedisTemplate.opsForGeo().add(key, new Point(116.334255, 40.027400), "xiaomi");
}
// 获取指定key中的元素的哈希值。key:指定的key,members:要获取哈希值的元素
@Nullable
List<String> hash(K key, M... members);
/**
* 获取地理位置的哈希值
*/
@Test
public void test_user_simple_geohash_hash(){
String key = "location";
List<String> position = stringRedisTemplate.opsForGeo().hash(key, "juejin", "ireader", "meituan", "jd", "xiaomi");
assert position != null;
position.forEach(System.out::println);
}
wx4gd94yjn0
wx4g52e1ce0
wx4gdg0tx40
wx4fk7jgtf0
wx4exqb0880
// 根据key和元素获取地理位置数据。key:指定的key,members:指定的元素
@Nullable
List<Point> position(K key, M... members);
/**
* 获取地理位置数据
*/
@Test
public void test_user_simple_geohash_get(){
String key = "location";
// 一次性获取多个,也可以获取一个
List<Point> position = stringRedisTemplate.opsForGeo().position(key, "juejin", "ireader", "meituan", "jd", "xiaomi");
assert position != null;
position.forEach(System.out::println);
}
Point [x=116.481050, y=39.996793]
Point [x=116.514202, y=39.905409]
Point [x=116.489032, y=40.007670]
Point [x=116.562106, y=39.787603]
Point [x=116.334253, y=40.027400]
// 计算两个元素之间的距离。key:指定的key,member1|member1:两个元素,Metric:计量单位,米、千米……
@Nullable
Distance distance(K key, M member1, M member1, Metric metric);
/**
* 计算两个元素之间的距离
*/
@Test
public void test_user_simple_geohash_distance(){
String key = "location";
Distance distance = stringRedisTemplate.opsForGeo().distance(key, "juejin", "ireader", RedisGeoCommands.DistanceUnit.KILOMETERS);
assert distance != null;
System.out.println(distance.getValue() + " KM");
}
10.5501 KM
// 查询指定元素附近的其它元素,指定范围查询,这个查询会返回自己
@Nullable
GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance, GeoRadiusCommandArgs args);
/**
* 查询指定元素附近的其它元素
*/
@Test
public void test_user_simple_geohash_radius(){
String key = "location";
// 获取距离掘金3km以内公司,范围查找,正序排列
RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.sortAscending()// 排序
.limit(2)// 输出元素的个数
.includeCoordinates()// 输出经纬度
.includeDistance();// 距离
// 封装距离参数
Distance distance = new Distance(3d, RedisGeoCommands.DistanceUnit.KILOMETERS);
// 获取值
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = stringRedisTemplate.opsForGeo().radius(key, "juejin", distance, geoRadiusCommandArgs);
assert radius != null;
radius.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + "\n 经纬度:" + s.getContent().getPoint() + "\n 距离:" + s.getDistance()));
}
名称:juejin
经纬度:Point [x=116.481050, y=39.996793]
距离:0.0 KILOMETERS
名称:meituan
经纬度:Point [x=116.489032, y=40.007670]
距离:1.3878 KILOMETERS
// 上面那个方法的一个重载方法
@Nullable
GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args);
/**
* todo 重载方法
* 查询指定元素附近的其它元素
*/
@Test
public void test_user_simple_geohash_radius_circle() {
String key = "location";
// 获取距离掘金3km以内公司,范围查找,正序排列
RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.sortAscending()// 排序
.limit(2)// 输出元素的个数
.includeCoordinates()// 输出经纬度
.includeDistance();// 距离
// 封装距离参数
Distance distance = new Distance(3d, RedisGeoCommands.DistanceUnit.KILOMETERS);
// 封装经纬度参数
Point point = new Point(116.48105,39.996794);
// 封装范围参数
Circle circle = new Circle(point, distance);
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = stringRedisTemplate.opsForGeo().radius(key, circle, geoRadiusCommandArgs);
assert radius != null;
// 获取到的距离可能回是科学计数法得到的
radius.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + "\n 经纬度:" + s.getContent().getPoint() + "\n 距离:" + s.getDistance()));
}
名称:juejin
经纬度:Point [x=116.481050, y=39.996793]
距离:1.0E-4 KILOMETERS
名称:meituan
经纬度:Point [x=116.489032, y=40.007670]
距离:1.3878 KILOMETERS
在一个地图应用中,车的数据、餐馆的数据、人的数据可能会有百万千万条,如果使用 Redis 的 Geo 数据结构,它们将全部放在一个 zset 集合中。在 Redis 的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个 key 的数据过大,会对集群的迁移工作造成 较大的影响,在集群环境中单个 key 对应的数据量不宜超过 1M,否则会导致集群迁移出现 卡顿现象,影响线上服务的正常运行。
所以,这里建议 Geo 的数据使用单独的 Redis 实例部署,不使用集群环境。
如果数据量过亿甚至更大,就需要对 Geo 数据进行拆分,按国家拆分、按省拆分,按 市拆分,在人口特大城市甚至可以按区拆分。这样就可以显著降低单个 zset 集合的大小。
- 创作不易, 非常欢迎大家的点赞、评论和关注(^_−)☆
- 你的点赞、评论以及关注
- 是对我最大的支持和鼓励
- 是我继续创作高质量博客的动力 !!!