redis-geo经纬度查询-从进到远的分页查询

redis-geo经纬度查询-从进到远的分页

背景:

最近接到一个需求是实现一个附近的人功能,目前几个主流数据库mysql、mongo、redis都有类似的功能,其原理都是建立空间索引再通过空间函数转化坐标点去查询,考虑到查询效率和数据不需要长期持久化,这里选择redis,底层由zset实现。

简单应用:

这里使用Spring自带的spring-data-redis库

  • 添加
/**
     *
     * @param key zsetKey
     * @param member 元素
     * @param lng 经度
     * @param lat 纬度
     * @return 影响数量
     */
@Override
public Long geoAdd(String key, Object member, double lng, double lat) {
  Long add = defaultRedisTemplate.opsForGeo().add(key, new Point(lng, lat), member);
  return add;
}
  • 删除

    因为redis-geo基于zset实现,所以直接使用zremove即可

public long zRemove(String key, Object member) {
	return defaultRedisTemplate.opsForZSet().remove(key, member);
}
  • 点-半径查询

    Circle 类有两个字段

    private final Point center; // 圆心
    private final Distance radius;// 半径
    
 /**
     * 
     * @param key 
     * @param circle 查询圆
     * @param limit 查询条数
     * @return
     */
@Override
public List<GeoResult<Object>> geoRadiusWithDist(String key, Circle circle, Long limit) {
  GeoResults results= defaultRedisTemplate.opsForGeo().radius(key,circle, 	RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(limit));
  return results.getContent();
}

分页快照查询:

上面介绍了基于点的半径查询,但实际中应用中可能数据量较大,不建议一次性返回,需要分页查询,下面介绍一种方法:

原理:先通过半径查询再缓存到一个zset中,再通过查询zset使用offset实现分页查询。参考命令:https://redis.io/commands/georadius/

由于目前使用的redisTemplate不支持redis的GEORADIUS命令带STOREDIST参数,所以基于lua脚本实现方案如下

  • 缓存快照

    /**
         * 查询并缓存快照
         * @param scriptKeys 脚本key
         * @param scriptParams 脚本参数
         * @return 影响条数
         举例命令:GEORADIUS geo-key 100 20 10000 km COUNT 100 STOREDIST testZset1
         解释:
       		ARGV[1]:geo的key:geo-key
       		ARGV[2]:经度:100
       		ARGV[3]:纬度:20
       		ARGV[4]:距离值:10000
       		ARGV[5]:距离单位:km
       		KEYS[1]:GEORADIUS命令自带COUNT参数(指定查询参数):COUNT 
       		ARGV[6]:取数量:100
       		KEYS[2]:GEORADIUS命令自带STOREDIST参数(将结果缓存到指定zset中):STOREDIST
       		ARGV[7]:指定的zset名
         */
    public Long geoRadiusAndStoreDist(List<String> scriptKeys, List<String> scriptParams) {
       String script = "return redis.call('GEORADIUS',ARGV[1],ARGV[2],ARGV[3],ARGV[4],ARGV[5],KEYS[1],ARGV[6],KEYS[2],ARGV[7])";
       return (Long) defaultRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), scriptParams);
     }
    

总结:

本文主要介绍了使用redis存储geo地理位置信息及基于spring-redis-data组件的查询方案,并且延伸了一种可分页查询的方案,主要解决两个方面的问题:
1、一次地理位置查询返回结果数量过多响应慢
2、将数据缓存为快照,可以保证多次使用offset分页查询不会有重复数据,且降低redis服务压力

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值