赞
踩
Redis 模块是Redis的高级功能,允许我们实现特定的自定义数据类型。本质上,模块是一个动态库,可以在启动时或根据命令按需加载到 Redis 中 MODULE LOAD 。模块可以用多种语言编写,包括 C 和 Rust。
我们自己使用 Redis 模块实现新的数据类型是一项艰巨的工作。值得庆幸的是,有许多流行且广泛使用的模块可以解决全文搜索(RediSearch)、时间序列处理(RedisTimeSeries)和原生 JSON 支持(RedisJSON)等问题。让我们概述一下一些使用更广泛的 Redis 模块。
本节提供了 RediSearch、RedisJSON 和 RedisTimeSeries 模块的高级概述,以及与它们相关的重要命令。
要完成本教程的这一部分,您需要 安装最新版本的 Go 和 Docker。所有上述模块都作为Redis Stack的一部分提供 ,它将 Redis 与各种相关的软件包和服务捆绑在一起。您可以使用 Docker 启动 Redis Stack 本地实例 :
docker run -d --name redis-stack -p 6379:6379 redis/redis-stack:latest
在开始之前,让我们创建一个 Go 模块来托管示例:
go mod init go-redis-modules
RedisJSON
在 Redis 中处理 JSON 数据时,传统方法有其局限性。默认情况下,Redis 将值视为简单字符串,缺乏对查询或操作 JSON 等结构化数据的内置支持。因此,开发人员经常将 JSON 存储为 字符串化 值,从而牺牲了对各个字段执行高效操作或利用嵌套结构的能力。这种方法需要解析整个 JSON 文档,即使是微小的更新,导致效率低下并增加处理开销。
RedisJSON 模块通过向 Redis 引入本机 JSON 处理功能来解决这些挑战。它允许您像任何其他本机数据类型一样存储、更新和检索 JSON。
RedisJSON 提供了一组丰富的 JSON 文档操作。以下是一些最常用的命令:
下面的示例演示了如何在 Go 应用程序中使用各种 RedisJSON 命令。请注意,我们使用的是 go-redis 客户端。
package main
import (
"context"
"errors"
"fmt"
"log"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
// JSON.SET
err := rdb.Do(ctx, "JSON.SET", "mydoc", ".", `{"name":"John","credits":30,"cars":["honda","toyota"]}`).Err()
if err != nil {
log.Fatal(err)
}
// JSON.GET
val, err := rdb.Do(ctx, "JSON.GET", "mydoc").Result()
if err != nil {
log.Fatal(err)
}
fmt.Println("JSON.GET result -", val)
// JSON.ARRAPPEND
_, err = rdb.Do(ctx, "JSON.ARRAPPEND", "mydoc", ".cars", `"audi"`).Int()
if err != nil {
log.Fatal(err)
}
fmt.Println("added audi to list of cars using JSON.ARRAPPEND")
// JSON.GET
val, err = rdb.Do(ctx, "JSON.GET", "mydoc").Result()
if err != nil {
log.Fatal(err)
}
fmt.Println("lastest document", val)
// JSON.NUMINCRBY
_, err = rdb.Do(ctx, "JSON.NUMINCRBY", "mydoc", ".credits", 5).Result()
if err != nil {
log.Fatal(err)
}
fmt.Println("incremented credits by 5 using JSON.NUMINCRBY")
// JSON.STRAPPEND
_, err = rdb.Do(ctx, "JSON.STRAPPEND", "mydoc", ".name", `", Jr."`).Int()
if err != nil {
log.Fatal(err)
}
fmt.Println("updated name using JSON.STRAPPEND")
// JSON.GET
val, err = rdb.Do(ctx, "JSON.GET", "mydoc").Result()
if err != nil {
log.Fatal(err)
}
fmt.Println("latest document", val)
// JSON.DEL
_, err = rdb.Do(ctx, "JSON.DEL", "mydoc").Int()
if err != nil {
log.Fatal(err)
}
fmt.Println("deleted document using JSON.DEL")
err = rdb.Do(ctx, "JSON.GET", "mydoc").Err()
if errors.Is(err, redis.Nil) {
fmt.Println("document 'mydoc' could not be found")
}
}
要运行上述代码,请将其复制到名为的文件中 main.go 并执行以下命令:
# get dependencies
go get github.com/redis/go-redis/v9
# run program
go run main.go
请注意,我们不需要获取整个 JSON 文档来更新它。RedisJSON 提供精细操作来处理 JSON 数据,从而提高应用程序性能(减少延迟)并降低成本(减少网络带宽)。
RediSearch
RediSearch模块通过强大的全文搜索功能增强了Redis。它引入了基于倒排索引的搜索引擎,允许高效且可扩展的文本搜索。使用 RediSearch,您可以索引文本数据、执行复杂的搜索查询并检索相关搜索结果。
它提供关键字搜索、精确短语匹配、布尔运算、分页、排序和过滤等功能。
RediSearch 根据搜索结果与查询的匹配程度对搜索结果进行评分,使您能够对搜索结果进行排名和确定优先级。
以下是一些最常用的 RediSearch 命令:
下面的示例演示了如何在 Go 应用程序中使用各种 RediSearch 命令。请注意redisearch-go客户端的用法 。
package main
import (
"fmt"
"math/rand"
"strconv"
"github.com/RediSearch/redisearch-go/redisearch"
"github.com/gomodule/redigo/redis"
)
var pool *redis.Pool
var client *redisearch.Client
var cities = []string{"new york", "london", "paris"}
func init() {
pool = &redis.Pool{Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "localhost:6379")
}}
client = redisearch.NewClientFromPool(pool, "user-index")
}
func main() {
schema := redisearch.NewSchema(redisearch.DefaultOptions).
AddField(redisearch.NewTextFieldOptions("name", redisearch.TextFieldOptions{})).
AddField(redisearch.NewTextFieldOptions("city", redisearch.TextFieldOptions{}))
indexDefinition := redisearch.NewIndexDefinition().AddPrefix("user:")
client.CreateIndexWithIndexDefinition(schema, indexDefinition)
fmt.Println("redisearch index created")
conn := pool.Get()
for i := 1; i <= 10; i++ {
hashName := "user:" + strconv.Itoa(i)
val := redis.Args{hashName}.AddFlat(map[string]string{"user_id": strconv.Itoa(i), "city": cities[rand.Intn(len(cities))]})
conn.Do("HSET", val...)
fmt.Println("created hash -", hashName)
}
docs, total, _ := client.Search(redisearch.NewQuery("*"))
fmt.Println("no. of indexed documents =", total)
docs, total, _ = client.Search(redisearch.NewQuery("@city:(paris|london)"))
fmt.Println("found", total, "users in london or paris")
for _, doc := range docs {
fmt.Println("document ID -", doc.Id)
fmt.Println("document attributes -", doc.Properties)
}
err := client.DeleteDocument("user:1")
if err != nil {
fmt.Println("failed to delete document (hash) user:1", err)
}
fmt.Println("deleted document (hash) user:1")
_, total, _ = client.Search(redisearch.NewQuery("*"))
fmt.Println("no. of indexed documents =", total)
err = client.DropIndex(true)
if err != nil {
fmt.Println("failed to drop index", err)
}
fmt.Println("dropped index and documents")
_, total, _ = client.Search(redisearch.NewQuery("*"))
fmt.Println("no. of indexed documents =", total)
}
要运行上述代码,请将其复制到名为的文件中 main.go 并执行以下命令:
go get github.com/RediSearch/redisearch-go/redisearch
go get github.com/gomodule/redigo/redis
go run main.go
我们使用 Hash 作为文档,但 RediSearch 也可以使用 RedisJSON。当添加或删除文档时,索引会自动更新。本示例演示了一些简单的操作,包括获取所有文档,以及通过城市属性上的 ORclause 进行过滤,以获取居住在 "伦敦 "或 "巴黎 "的用户。
Redis时间序列
在 Redis 中处理大量时间序列数据时,有一些常用的技术。这些涉及将数据存储在 Redis 排序集中,其中分数表示时间戳,成员表示数据值。另一种方法是使用带有时间戳的字符串键作为键名称的一部分。每个键值对代表时间序列中的一个特定数据点。
然而,这些技术在处理大量时间序列数据时存在一些局限性。传统的 Redis 数据结构(如字符串键或排序集)的查询能力有限,并且难以执行复杂的聚合。此外,在处理大型数据集时,将每个数据点存储为单独的键值对可能会消耗大量内存,这通常是时间序列的情况。
RedisTimeSeries模块是原生的时序数据结构,为时序数据提供高效的存储和查询能力。它添加了查询功能,例如检索某个时间范围内的数据、执行聚合(例如求和、计数、平均值)、下采样和插值。它还允许我们定义自动过期或缩减数据采样的保留策略,从而实现高效的数据保留并降低存储要求。RedisTimeSeries 还与生态系统中的其他工具集成,例如 Prometheus 和 Grafana。
RedisTimeSeries 提供了丰富的命令集。以下是一些最常用的:
下面的示例演示了如何在 Go 应用程序中使用各种 RedisTimeSeries 命令。请注意,我们使用的是 redistimeseries-go 客户端。
package main
import (
"fmt"
"log"
"math/rand"
"time"
redistimeseries "github.com/RedisTimeSeries/redistimeseries-go"
)
const tsName = "temperature:living_room"
func main() {
client := redistimeseries.NewClient("localhost:6379", "", nil)
// TS.CREATE
err := client.CreateKeyWithOptions(tsName, redistimeseries.DefaultCreateOptions)
if err != nil {
log.Fatal(err)
}
fmt.Println("created time series key", tsName)
tempOptions := []float64{24, 24.5, 25}
var storedTS []int64
for i := 1; i <= 10; i++ {
// TS.ADD
res, err := client.AddAutoTs(tsName, tempOptions[(rand.Intn(len(tempOptions)))])
storedTS = append(storedTS, res)
if err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Millisecond)
}
fmt.Println("added time series data")
// TS.GET
lastDataPoint, err := client.Get(tsName)
if err != nil {
log.Fatal(err)
}
fmt.Printf("lastest data point - timestamp: %v, temp: %v\n", lastDataPoint.Timestamp, lastDataPoint.Value)
// TS.RANGE
dataPoints, err := client.RangeWithOptions(tsName, storedTS[0], storedTS[len(storedTS)-1], redistimeseries.DefaultRangeOptions)
if err != nil {
log.Fatal(err)
}
fmt.Println("TS.RANGE result")
for _, dp := range dataPoints {
fmt.Printf("timestamp: %v, temp: %v\n", dp.Timestamp, dp.Value)
}
}
要运行上述代码,请将其复制到名为的文件中 main.go 并执行以下命令:
go get github.com/RedisTimeSeries/redistimeseries-go
go run main.go
Redis 模块:最佳实践
本节介绍了一些在这些模块的实际部署中优化可扩展性和性能的最佳实践。
RedisJSON
Redis搜索
Redis时间序列
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。