redis也可以根据经纬度查询附近的元素以及计算两个经纬度的距离???

前言

点赞在看,养成习惯。

点赞收藏,人生辉煌。

点击关注【微信搜索公众号:编程背锅侠】,防止迷路。

使用场景

业界比较通用的地理位置距离排序算法是 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 radius(K key, M member, Distance distance, GeoRadiusCommandArgs args); 查询指定元素附近的其它元素
GeoResults radius(K key, Circle within, GeoRadiusCommandArgs args); 查询指定元素附近的其它元素-重载方法

Redis中 的 Geo指令基本使用

添加经纬度元素命令

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"

代码中使用Redis中 的 Geo

在Reids中的加入几个经纬度数据

源码解读

// 根据指定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:指定的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 集合的大小。

谢谢点赞

  • 创作不易, 非常欢迎大家的点赞、评论和关注(^_−)☆
  • 你的点赞、评论以及关注
  • 是对我最大的支持和鼓励
  • 是我继续创作高质量博客的动力 !!!

你可能感兴趣的:(数据飞升,redis中opsForGeo,redis查询附近的人,redis中查询经纬度的距离,redis插叙两个经纬度距离,redis中geohash用法)