在上一篇文章中,我们详细介绍了如何在 Golang 中使用 go-redis 操作 Redis 的事务处理操作(可参考这里)。在本篇文章中,我们将对如何使用 go-redis 操作 Redis 中的 GEO 地理空间数据结构进行详细讲解和示例代码展示。

👉 点击查看《go-redis使用指南》系列文章目录

在《go-redis使用指南》系列文章中,我们将详细介绍如何在 Golang 项目中使用 redis/go-redis 库与 Redis 进行交互。以下是该系列文章的全部内容:

  1. Golang 操作 Redis:快速上手 - go-redis 使用指南
  2. Golang 操作 Redis:连接设置与参数详解 - go-redis 使用指南
  3. Golang 操作 Redis:基础的字符串键值操作 - go-redis 使用指南
  4. Golang 操作 Redis:如何设置 key 的过期时间 - go-redis 使用指南
  5. Golang 操作 Redis:Hash 哈希数据类型操作用法 - go-redis 使用指南
  6. Golang 操作 Redis:Set 集合数据类型操作用法 - go-redis 使用指南
  7. Golang 操作 Redis:为 Hash 中的字段设置过期时间 - go-redis 使用指南
  8. Golang 操作 Redis:List 列表数据类型操作用法 - go-redis 使用指南
  9. Golang 操作 Redis:SortedSet 有序集合数据类型操作用法 - go-redis 使用指南
  10. Golang 操作 Redis:bitmap 数据类型操作用法 - go-redis 使用指南
  11. Golang 操作 Redis:事务处理操作用法 - go-redis 使用指南
  12. Golang 操作 Redis:地理空间数据类型操作用法 - go-redis 使用指南
  13. Golang 操作 Redis:HyperLogLog 操作用法 - go-redis 使用指南
  14. Golang 操作 Redis:Pipeline 操作用法 - go-redis 使用指南
  15. Golang 操作 Redis:PubSub发布订阅用法 - go-redis 使用指南
  16. Golang 操作 Redis:布隆过滤器(Bloom Filter)操作用法 - go-redis 使用指南
  17. Golang 操作 Redis:Cuckoo Filter操作用法 - go-redis 使用指南
  18. Golang 操作 Redis:Stream操作用法 - go-redis 使用指南
golang redis go-redis

Redis GEO 地理空间数据结构简介

Redis 的 GEO 地理空间数据结构允许我们存储、查询和操作地理空间信息。通过 GEO 命令,我们可以实现类似于地理信息系统(GIS)的一些功能,比如添加地理位置、计算距离、查询附近的点等。

常见使用场景:

  1. 位置存储与查询:存储用户、商店或其他实体的地理位置,并能快速查询某一位置附近的其他位置。
  2. 距离计算:计算两个地理位置之间的距离。
  3. 地理围栏:判断某个位置是否在特定区域内等。

Redis GEO 与 Elasticsearch GEO 的对比

在实际应用中,Elasticsearch 通常比 Redis 更常用于地理空间搜索(geo search),Redis 的 GEO 功能相对简单,主要用于半径范围内的基本地理位置查找和简单的排序、距离计算,适合作为简单业务处理小规模地理数据。Elasticsearch 支持多种查询类型,如基于半径的搜索、矩形搜索、多边形搜索等,还能处理复杂的地理过滤条件,它采用文档存储系统,不像 Redis 会受限于内存容量,天然支持横向扩展,能够处理大规模的数据量。

go-redis 中 GEO 地理空间操作的方法

以下是 go-redis 库中用于 GEO 地理空间操作的方法及其功能描述:

  • GeoAdd - 添加地理空间位置
  • GeoPos - 获取地理空间位置
  • GeoRadius - 根据经纬度查询附近的地理位置
  • GeoRadiusStore - 根据经纬度查询附近的地理位置并存储结果
  • GeoRadiusByMember - 根据已有成员的位置查询附近的地理位置
  • GeoRadiusByMemberStore - 根据已有成员的位置查询附近的地理位置并存储结果
  • GeoSearch - 根据不同条件搜索地理位置
  • GeoSearchLocation - 根据不同条件搜索地理位置并返回详细信息
  • GeoSearchStore - 根据不同条件搜索地理位置并存储结果
  • GeoDist - 计算两个地理位置之间的距离
  • GeoHash - 获取地理位置的哈希表示

go-redis GEO 地理空间操作方法详细讲解及示例代码

GeoAdd:将一个或多个地理空间位置添加到指定的 key 中。

方法签名

GeoAdd(ctx context.Context, key string, geoLocation ...*GeoLocation) *IntCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • geoLocation:一个或多个地理位置,类型为*GeoLocation,包含经纬度和成员名称。

返回结果说明: 返回类型为*IntCmd,表示成功添加的地理位置的数量。

示例代码

package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

func main() {
	ctx := context.Background()
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	locations := []*redis.GeoLocation{
		{Name: "PalaceMuseum", Longitude: 116.4039, Latitude: 39.915, Dist: 0, GeoHash: 0},
		{Name: "SummerPalace", Longitude: 116.3975, Latitude: 39.9087, Dist: 0, GeoHash: 0},
	}

	result, err := rdb.GeoAdd(ctx, "china:beijing", locations...).Result()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Number of locations added: %d\n", result)
}

*输出结果:

Number of locations added: 2

可以看到其实底层是以 zset 作为存储的:

go-redis geo

GeoPos:获取指定成员的地理位置。

方法签名

GeoPos(ctx context.Context, key string, members ...string) *GeoPosCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • members:一个或多个成员名称。

返回结果说明: 返回类型为*GeoPosCmd,包含成员的经纬度信息。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    pos, err := rdb.GeoPos(ctx, "china:beijing", "PalaceMuseum", "SummerPalace").Result()
    if err != nil {
        panic(err)
    }

    for i, p := range pos {
        if p != nil {
            fmt.Printf("Position of member %d: (%f, %f)\n", i, p.Longitude, p.Latitude)
        } else {
            fmt.Printf("Position of member %d not found\n", i)
        }
    }
}

*输出结果:

Position of member 0: (116.403900, 39.915000)
Position of member 1: (116.397500, 39.908700)

GeoRadius:根据经纬度查询附近的地理位置。

方法签名

GeoRadius(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • longitude:查询中心点的经度。
  • latitude:查询中心点的纬度。
  • query:查询参数,类型为*GeoRadiusQuery,包含半径、单位等。

返回结果说明: 返回类型为*GeoLocationCmd,包含查询到的地理位置的详细信息。

示例代码

package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

func main() {
	ctx := context.Background()
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	query := &redis.GeoRadiusQuery{
		Radius:      1,
		Unit:        "km", // Can be m, km, ft, or mi. Default is km.
		WithCoord:   true,
		WithDist:    true,
		WithGeoHash: true,
		Count:       10,
		Sort:        "asc", // Can be ASC or DESC. Default is no sort order.
	}

	locations, err := rdb.GeoRadius(ctx, "china:beijing", 116.4030, 39.911, query).Result()
	if err != nil {
		panic(err)
	}

	for _, loc := range locations {
		fmt.Printf("location: %+v\n", loc)
	}
}

*输出结果:

location: {Name:PalaceMuseum Longitude:116.40389889478683 Latitude:39.91500057149189 Dist:0.4515 GeoHash:4069885555039198}
location: {Name:SummerPalace Longitude:116.3974991440773 Latitude:39.90869925468977 Dist:0.5345 GeoHash:4069885365519944}

GeoRadiusStore:根据经纬度查询附近的地理位置,并将结果存储到指定的 key 中。

方法签名

GeoRadiusStore(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • longitude:查询中心点的经度。
  • latitude:查询中心点的纬度。
  • query:查询参数,类型为*GeoRadiusQuery,包含半径、单位等。

返回结果说明: 返回类型为*IntCmd,表示存储到新 key 中的位置数量。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    query := &redis.GeoRadiusQuery{
        Radius: 1, // 1公里半径
        Unit:   "km",
        Store:  "china:beijing:nearby", // 存储结果的key
    }

    result, err := rdb.GeoRadiusStore(ctx, "china:beijing", 116.4039, 39.915, query).Result()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Number of locations stored: %d\n", result)
}

*输出结果:

Number of locations stored: 2

GeoRadiusByMember:根据已有成员的位置查询附近的地理位置。

方法签名

GeoRadiusByMember(ctx context.Context, key, member string, query *GeoRadiusQuery) *GeoLocationCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • member:已有成员名称。
  • query:查询参数,类型为*GeoRadiusQuery,包含半径、单位等。

返回结果说明: 返回类型为*GeoLocationCmd,包含查询到的地理位置的详细信息。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

	query := &redis.GeoRadiusQuery{
		Radius:      1,
		Unit:        "km",
		WithCoord:   true,
		WithDist:    true,
		WithGeoHash: true,
		Sort:        "asc",
	}

	locations, err := rdb.GeoRadiusByMember(ctx, "china:beijing", "PalaceMuseum", query).Result()
	if err != nil {
		panic(err)
	}

	for _, loc := range locations {
		fmt.Printf("location: %+v\n", loc)
	}
}

*输出结果:

location: {Name:PalaceMuseum Longitude:116.40389889478683 Latitude:39.91500057149189 Dist:0 GeoHash:4069885555039198}
location: {Name:SummerPalace Longitude:116.3974991440773 Latitude:39.90869925468977 Dist:0.8884 GeoHash:4069885365519944}

GeoRadiusByMemberStore:根据已有成员的位置查询附近的地理位置,并将结果存储到指定的 key 中。

方法签名

GeoRadiusByMemberStore(ctx context.Context, key, member string, query *GeoRadiusQuery) *IntCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • member:已有成员名称。
  • query:查询参数,类型为*GeoRadiusQuery,包含半径、单位等。

返回结果说明: 返回类型为*IntCmd,表示存储到新 key 中的位置数量。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    query := &redis.GeoRadiusQuery{
        Radius: 1, // 1公里半径
        Unit:   "km",
        Store:  "china:beijing:nearby", // 存储结果的key
    }

    result, err := rdb.GeoRadiusByMemberStore(ctx, "china:beijing", "PalaceMuseum", query).Result()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Number of locations stored: %d\n", result)
}

*输出结果:

Number of locations stored: 2

GeoSearch:根据不同条件搜索地理位置。

方法签名

GeoSearch(ctx context.Context, key string, q *GeoSearchQuery) *StringSliceCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • q:查询参数,类型为*GeoSearchQuery,包含中心点、半径或边界框、排序等。

返回结果说明: 返回类型为*StringSliceCmd,表示查询到的成员名称。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

	query := &redis.GeoSearchQuery{
		Member: "PalaceMuseum",
	}

	member, err := rdb.GeoSearch(ctx, "china:beijing", query).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Found member: %+v\n", member)

	query = &redis.GeoSearchQuery{
		Member:     "",
		Longitude:  116.4039,
		Latitude:   39.915,
		Radius:     1,
		RadiusUnit: "km",
		BoxWidth:   0.0,
		BoxHeight:  0.0,
		BoxUnit:    "",
		Sort:       "asc",
		Count:      0,  // 指定查询结果返回的最大数量。
		CountAny:   false,  // CountAny为false或未设置,则只会返回精确匹配的结果,数量等于Count指定的数量。当CountAny设置为true时,Redis将尽可能返回接近Count指定数量的结果,允许结果集包含不精确的匹配。
	}
	members, err := rdb.GeoSearch(ctx, "china:beijing", query).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Found members: %+v\n", members)
}

*输出结果:

Found member: [PalaceMuseum]
Found members: [PalaceMuseum SummerPalace]

GeoSearchLocation:根据不同条件搜索地理位置,并返回详细信息。

方法签名

GeoSearchLocation(ctx context.Context, key string, q *GeoSearchLocationQuery) *GeoSearchLocationCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • q:查询参数,类型为*GeoSearchLocationQuery,包含中心点、半径或边界框、排序等。

返回结果说明: 返回类型为*GeoSearchLocationCmd,包含查询到的地理位置的详细信息。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

	query := &redis.GeoSearchLocationQuery{
		GeoSearchQuery: redis.GeoSearchQuery{
			Member: "PalaceMuseum",
		},
		WithCoord: true,
		WithDist:  true,
		WithHash:  true,
	}

	member, err := rdb.GeoSearchLocation(ctx, "china:beijing", query).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Found member: %+v\n", member)

	query = &redis.GeoSearchLocationQuery{
		GeoSearchQuery: redis.GeoSearchQuery{
			Member:     "",
			Longitude:  116.4039,
			Latitude:   39.915,
			Radius:     1, // 1公里半径
			RadiusUnit: "km",
			BoxWidth:   0.0,
			BoxHeight:  0.0,
			BoxUnit:    "",
			Sort:       "asc",
			Count:      0,
			CountAny:   false,
		},
		WithCoord: true,
		WithDist:  true,
		WithHash:  true,
	}
	members, err := rdb.GeoSearchLocation(ctx, "china:beijing", query).Result()
	if err != nil {
		panic(err)
	}

	for i, member := range members {
		fmt.Printf("Found member%d: %+v\n", i, member)
	}
}

*输出结果:

Found member: [{Name:PalaceMuseum Longitude:116.40389889478683 Latitude:39.91500057149189 Dist:0 GeoHash:4069885555039198}]
Found member0: {Name:PalaceMuseum Longitude:116.40389889478683 Latitude:39.91500057149189 Dist:0.0001 GeoHash:4069885555039198}
Found member1: {Name:SummerPalace Longitude:116.3974991440773 Latitude:39.90869925468977 Dist:0.8884 GeoHash:4069885365519944}

GeoDist:计算两个地理位置之间的距离。

方法签名

GeoDist(ctx context.Context, key string, member1, member2, unit string) *FloatCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • member1:第一个成员名称。
  • member2:第二个成员名称。
  • unit:距离单位,如m(米)、km(公里)、mi(英里)、ft(英尺)。

返回结果说明: 返回类型为*FloatCmd,表示计算得到的距离。

示例代码

package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

func main() {
	ctx := context.Background()
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	dist, err := rdb.GeoDist(ctx, "china:beijing", "PalaceMuseum", "SummerPalace", "km").Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Distance between PalaceMuseum and SummerPalace: %f km\n", dist)
}

*输出结果:

Distance between PalaceMuseum and SummerPalace: 0.888400 km

GeoHash:获取地理位置的哈希表示。

方法签名

GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd

参数说明

  • ctx:上下文参数,用于控制请求的生命周期。
  • key:存储地理空间数据的 key。
  • members:一个或多个成员名称。

返回结果说明: 返回类型为*StringSliceCmd,表示成员的地理位置的哈希值。

示例代码

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    hashes, err := rdb.GeoHash(ctx, "china:beijing", "PalaceMuseum", "SummerPalace").Result()
    if err != nil {
        panic(err)
    }

    for i, hash := range hashes {
        fmt.Printf("GeoHash of member %d: %s\n", i, hash)
    }
}

*输出结果:

GeoHash of member 0: wx4g0f69xr0
GeoHash of member 1: wx4g09nj420

结语

通过本文,我们详细了解了 Redis 中的 GEO 地理空间数据结构及其常见使用场景,并使用 go-redis 库演示了相关的操作方法。希望这篇文章能够帮助你更好地在项目中应用 Redis 的地理空间功能,点击 go-redis 使用指南 可查看更多相关教程!


也可以看看