Redis那些事儿(三)-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

文章目录

1. 前言

        接着上一篇Redis那些事儿二 这一篇主要介绍Redis基于Geo数据结构实现的地理服务它提供了一种方便的方式来存储和处理与地理位置相关的数据。Geo数据结构是Redis的一种特殊数据类型用于存储地理位置信息每个地理位置被表示为经度和纬度的坐标可以将这些坐标与一个或多个成员关联起来。Redis的地理服务提供了一套简单而强大的功能可以方便地存储和处理与地理位置相关的数据它适用于许多应用场景如地理定位、附近的人、附近的店铺搜索、附近的停车场、附近的地铁站…等等大大提升了定位排序的效率。

2. 常用api介绍

        Redis地理服务API方法包括GEOADD向Geo数据结构中添加一个或多个地理位置信息GEODIST计算两个地理位置之间的距离GEORADIUS获取给定地理位置附近一定范围内的成员GEOPOS获取给定成员的经纬度坐标GEOHASH获取给定成员的Geohash值…以上都是Geo地理服务内置的常用方法接下来还是基于开发中的StringRedisTemplate对象作为切入点更直观地说明实际应用中对于Geo地理服务地应用。
        StringRedisTemplate中定义了RedisGeoCommands的接口RedisGeoCommands中封装了一系列的内置方法及子类所以Redis中基于opsForGeo()的操作都离不开RedisGeoCommands如下为部分截图
RedisGeoCommands部分截图

Geo数据结构中存入坐标数据redisTemplate.opsForGeo().add(key, locations)

		List<Park> parks = getParks(); //TODO 获取停车场列表信息
    	//初始化Redis区域对象集合
        List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>();
        for (Park park : parks) {
        	//实例化ponit对象传参[经度、纬度]
            Point point = new Point(park.getLng(), park.getLat());
            //构造location对象传参[name值一般取ID、point对象]
            RedisGeoCommands.GeoLocation<String> location = new RedisGeoCommands.GeoLocation<>(
                   park.getParkId() + "", point);
            locations.add(location);
        }
        String key = "GEO_PARK_KEY";
        //存入坐标数据
        redisTemplate.opsForGeo().add(key, locations);

Geo数据结构中删除坐标数据redisTemplate.opsForGeo().remove(key, …members)

		String key = "GEO_PARK_KEY";
		//删除单个坐标数据parkId为单个停车场ID类型为String
		redisTemplate.opsForGeo().remove(key, parkId);
    	//删除多个坐标数据第一个参数为key后面可以传入多个parkId
    	redisTemplate.opsForGeo().remove(key, parkId1, parkId2, parkId3);

Geo数据结构中检索坐标数据由近到远redisTemplate.opsForGeo().radius(key, within, args)

		String key = "GEO_PARK_KEY";
		//检索20公里内的
		Distance distance = new Distance(20, Metrics.KILOMETERS);
		//以目标坐标为圆心distance为半径的圆圈范围其中lng代表中心坐标的经度、lat代表中心坐标的纬度
		Circle within = new Circle(new Point(lng, lat), distance);
		//条件参数按照距离查询默认就是升序
		RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance();
		//执行查询
    	GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, within, args);

Geo数据结构中检索坐标数据由近到远redisTemplate.opsForGeo().search(key, reference, distance, args)

		String key = "GEO_PARK_KEY";
		//检索20公里内的
		Distance distance = new Distance(20, Metrics.KILOMETERS);
		//条件参数按照距离查询默认就是升序
		RedisGeoCommands.GeoSearchCommandArgs args = RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance();
		//中心点位确认其中lng代表中心坐标的经度、lat代表中心坐标的纬度
		GeoReference<String> reference = GeoReference.fromCoordinate(lng, lat);
		//执行查询
    	GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().search(key, reference, distance, args);

        以上列了四个最常用的方式类似于我们最常规的CURD其中最后两个radius和search是查询方法二者最终查询的结果是一致的只是手段方式不同而已

3. 需求假设获取离我最近的停车场

        这个时候有人就说了我使用GeodeticCalculator工具类在代码中计算距离也很方便的啊。我想了想确实很方便只需要引入geodesy的依赖就可以直接使用GeodeticCalculator的calculateGeodeticCurve方法就可以计算了还不需要麻烦的用redis搞那么长的代码了。但是问题来了就以停车场为例假如只有十几个停车场遍历一下然后按升序排个序很快就计算出了离我最近的停车场列表了。如果我有上千个或者上万个停车场总不能遍历上万次然后再排序吧那这速度就一言难尽了…如果这个时候使用redis的geo数据结构来读取那就完美解决这个问题了。Redis提供的GeoHash算法功能对于这方面的需求就太好用了那么附近的停车场、附近的人、附近的商家就都是一个思路了

4. 代码示例

	/**
     * 获取距离最近的停车场列表由近到远
     * @param lng 当前位置经度
     * @param lat 当前位置纬度
     * @param page 页数第n页
     * @param size 每页数量10、20...
     * @param value 公里范围内搜索范围半径
     * @return
     */
    public List<Park> getLatestParks(Double lng, Double lat, Integer page, Integer size, Double value){
        //计算分页起始参数
        Integer start = (page - 1) * size;
        Integer end = page * size;
        //查询redis按照距离排序
        String key = "GEO_PARK_KEY";
        //检索value公里内的
        Distance distance = new Distance(value, Metrics.KILOMETERS);
        //以目标坐标为圆心distance为半径的圆圈范围其中lng代表中心坐标的经度、lat代表中心坐标的纬度
        Circle within = new Circle(new Point(lng, lat), distance);
        //条件参数按照距离查询默认就是升序截止到end
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(end);
        //执行查询
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, within, args);
        if (results == null) {
            return new ArrayList<>();
        }
        //获取最终检索的内容
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
        //截取从起始到结束如果总数小于起始数就证明已经页数超了返回空集合
        if (content.size() <= start) {
            return new ArrayList<>();
        }
        //初始化parkId集合
        List<Long> parkIds = new ArrayList<>();
        //初始化距离map
        Map<String, Distance> distanceMap = new HashMap<>();
        //分页跳过之前的数据并遍历赋值
        content.stream().skip(start).forEach(i->{
            String parkIdStr = i.getContent().getName();
            parkIds.add(Long.valueOf(parkIdStr));
            Distance dis = i.getDistance();
            distanceMap.put(parkIdStr, dis);
        });
        //固定排序
        String join = StringUtils.join(parkIds,",");
        //根据parkId集合获取park集合
        List<Park> newParks = parkService.list(new QueryWrapper<Park>().in("park_id", parkIds)
                .last("ORDER BY FIELD(park_id," + join + ")")).stream().map(i -> {
                    i.setDistance(distanceMap.get(i.getParkId() + "").getValue());
                    return i;
        }).collect(Collectors.toList());
        return newParks;
    }

以上代码为由近到远获取距离最近的停车场列表的示例方法仅供参考

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: redis