Redis Geo操作地理位置

Redis Geo

Redis Geo 是Redis在3.2版本中新增的功能,用于存储和操作地理位置信息

使用场景

  1. 滴滴打车:这是一个对地理位置精度要求较高的场景。通过使用Redis的GEO功能,滴滴打车可以存储房源和店铺的地理位置信息,并根据用户所在位置的经纬度,加上范围,查询到附近的房源和店铺列表,放到高德地图中展现出来。
  2. 直播业务:比如主播开播的时候写入主播Id的经纬度,关播的时候删除主播Id元素,这样就维护了一个具有位置信息的在线主播集合提供给线上检索。
  3. 自如、蛋壳、链家、美团等平台也有根据距离找房源或者商铺的功能,这个功能也是使用的Redis的GEO功能。

API列表名词

字段 含义
longitude 经度
latitude 纬度
member 位置名称
radius 距离中心位置的距离
m/km/ft/mi 距离中心位置的单位,米/千米/英里/英尺
WITHCOORD 返回距离中心位置元素及经纬度
WITHDIST 返回距离中心位置元素及距离
WITHHASH 返回距离中心位置元素及geohash 值
COUNT 返回距离中心位置元素的元素个数
ASC/DESC 距离远近排序
STORE key 返回的集合元素存储到某个key中
STOREDIST key 返回的元素距离集合存储到某个key中
BYRADIUS 按圆形扫描
BYBOX 按矩形扫描

API列表

名称 含义 指令
GEOADD 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中 GEOADD key longitude latitude member [longitude latitude member ...]
GEOPOS 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度) GEOPOS key member [member ...]
GEOHASH 用于获取一个或多个位置元素的 geohash 值 GEOHASH key member [member ...]
GEODIST 用于返回两个给定位置之间的距离 GEODIST key member1 member2 [m/km/ft/mi]
GEORADIUS 以给定的经纬度为中心, 返回键包含的位置元素中, 与中心的距离不超过给定最大距离(radius)的所有位置元素 GEORADIUS key longitude latitude radius [m/km/ft/mi] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC
GEORADIUSBYMEMBER 与GEORADIUS 相似,只是该指令是以位置(member)为中心 GEORADIUSBYMEMBER key member radius [m/km/ft/mi] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC/DESC] [STORE key] [STOREDIST key]
GEOSEARCH 存在高版本Redis中,GEORADIUS升级,除了可以设置扫描范围,还可以设置扫描形状(圆形,矩形),geosearch的功能更加强大和灵活,可以满足更多的使用场景和需求 GEOSEARCH key <member / longitude latitude> <[BYRADIUS radius [m/km/ft/mi] ]/ [BYBOX width height [m/km/ft/mi]]> [ASC/DESC] [COUNT count] [WITHCOORD] [WITHDIST][WITHHASH]

Springboot使用

maven

xml 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

yaml

yaml 复制代码
spring:
  redis:
    host: 127.0.0.1 # Redis 服务器地址
    port: 6379 # Redis 服务器端口
    database: 0 # Redis 数据库索引(默认为0)

Test

java 复制代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.Metrics;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyDemoApplicationTests {

    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
     */
    @Test
    public void redisTestAdd() {
        Long a = redisTemplate.opsForGeo().add("geo", new Point(13.261391222476959, 38.215556214674542), "a");//params: key, Point(经度, 纬度), 地方名称
        Long b = redisTemplate.opsForGeo().add("geo", new Point(15.087267458438873, 37.50266842333162), "b");//params: key, Point(经度, 纬度), 地方名称
        System.out.println(a);
        System.out.println(b);
    }

    /**
     * 从key里返回所有给定位置元素的位置(经度和纬度)。
     */
    @Test
    public void redisTestGeoGet() {
        List<Point> points = redisTemplate.opsForGeo().position("geo", "a", "b");//params: key, 地方名称...
        System.out.println(points);
    }

    /**
     * 返回两个给定位置之间的距离。
     */
    @Test
    public void testDist() {
        Distance distance = redisTemplate.opsForGeo().distance("geo", "a", "b", RedisGeoCommands.DistanceUnit.KILOMETERS);//params: key, 地方名称1, 地方名称2, 距离单位
        System.out.println(distance);
    }

    /**
     * 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。
     */
    @Test
    public void redisTestNearByXY() {
        Circle circle = new Circle(new Point(114.05, 22.55), new Distance(200, Metrics.KILOMETERS));//Point(经度, 纬度) Distance(距离量, 距离单位)
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5);
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius("geo", circle, args);//params: key, Circle, GeoRadiusCommandArgs
        System.out.println(results);
    }

    /**
     * 以给定的城市为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。
     */
    @Test
    public void testNearByPlace() {
        Distance distance = new Distance(200, Metrics.KILOMETERS);//params: 距离量, 距离单位
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5);
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius("geo", "a", distance, args);//params: key, 地方名称, Circle, GeoRadiusCommandArgs
        System.out.println(results);
    }

    /**
     * 返回一个或多个位置元素的 Geohash 表示
     */
    @Test
    public void testGeoHash() {
        List<String> results = redisTemplate.opsForGeo().hash("geo", "a", "b");//params: key, 地方名称...
        System.out.println(results);
    }

}

注意事项

  • 在Redis的集群环境中,不建议将大量的数据存储在一个zset集合中,因为这会导致集群迁移时出现卡顿现象,影响线上服务的正常运行。如果数据量过大,需要对数据进行拆分,按国家、省份、城市等拆分。
  • 建议将频繁访问的数据存储在Redis中,而将低频数据存储在其他数据库中。
  • 避免将不相关的数据业务都放到一个Redis中,这可以避免业务相互影响,避免单实例膨胀,并能在故障时降低影响面,快速恢复。
  • 由于Redis是单线程服务,消息过大会阻塞并拖慢其他操作。因此,需要保持消息内容在1KB以下,严禁超过50KB的单条记录。
相关推荐
李少兄1 小时前
解决Spring Boot整合Redis时的连接问题
spring boot·redis·后端
日里安1 小时前
8. 基于 Redis 实现限流
数据库·redis·缓存
sam-12310 小时前
k8s上部署redis高可用集群
redis·docker·k8s
看山还是山,看水还是。11 小时前
Redis 配置
运维·数据库·redis·安全·缓存·测试覆盖率
谷新龙00111 小时前
Redis运行时的10大重要指标
数据库·redis·缓存
精进攻城狮@11 小时前
Redis缓存雪崩、缓存击穿、缓存穿透
数据库·redis·缓存
avenue轩16 小时前
gdb调试redis。sudo
c++·redis
不惑_16 小时前
Redis:发布(pub)与订阅(sub)实战
前端·redis·bootstrap
cui_win16 小时前
Redis高可用-Sentinel(哨兵)
redis·bootstrap·sentinel
cui_win19 小时前
Redis高可用-主从复制
redis·git·github·主从复制·哨兵