redis/go-redis

GitHub: redis/go-redis

Redis 官方出品的 Go 语言客户端,提供类型安全的 API、完善的连接池和全功能 Redis 支持。

Stars: 21950 | Forks: 2544

# Go 的 Redis 客户端 [![build workflow](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/d56642408e192951.svg)](https://github.com/redis/go-redis/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/redis/go-redis/v9)](https://pkg.go.dev/github.com/redis/go-redis/v9?tab=doc) [![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.io/docs/latest/develop/clients/go/) [![Go Report Card](https://goreportcard.com/badge/github.com/redis/go-redis/v9)](https://goreportcard.com/report/github.com/redis/go-redis/v9) [![codecov](https://codecov.io/github/redis/go-redis/graph/badge.svg?token=tsrCZKuSSw)](https://codecov.io/github/redis/go-redis) [![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/W4txy5AeKM) [![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) [![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) [![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) [![Stack Exchange questions](https://img.shields.io/stackexchange/stackoverflow/t/go-redis?style=social&logo=stackoverflow&label=Stackoverflow)](https://stackoverflow.com/questions/tagged/go-redis) ## 支持的版本 在 `go-redis` 中,我们的目标是支持 Redis 的最后三个版本。目前,这意味着我们确实支持: - [Redis 8.0](https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES) - 使用 Redis CE 8.0 - [Redis 8.2](https://raw.githubusercontent.com/redis/redis/8.2/00-RELEASENOTES) - 使用 Redis CE 8.2 - [Redis 8.4](https://raw.githubusercontent.com/redis/redis/8.4/00-RELEASENOTES) - 使用 Redis CE 8.4 尽管 `go.mod` 声明至少需要 `go 1.24`,但我们的 CI 配置为针对所有三个 Redis 版本和多个 Go 版本([1.24](https://go.dev/doc/devel/release#go1.24.0)、oldstable 和 stable)运行测试。我们注意到,某些与模块相关的测试可能无法通过 Redis Stack 7.2,并且某些命令在 Redis CE 8.0 中已更改。 尽管并未得到官方支持,`go-redis/v9` 应该能够与任何 Redis 7.0+ 配合使用。 如果您遇到任何问题,请务必查阅文档和测试。 ## 我该如何使用 Redis? [在 Redis University 免费学习](https://university.redis.com/) [使用 Redis Launchpad 更快构建](https://launchpad.redis.com/) [试用 Redis Cloud](https://redis.com/try-free/) [深入开发者教程](https://developer.redis.com/) [加入 Redis 社区](https://redis.com/community/) [在 Redis 工作](https://redis.com/company/careers/jobs/) ## 资源 - [讨论](https://github.com/redis/go-redis/discussions) - [聊天](https://discord.gg/W4txy5AeKM) - [参考](https://pkg.go.dev/github.com/redis/go-redis/v9) - [示例](https://pkg.go.dev/github.com/redis/go-redis/v9#pkg-examples) ## 旧版文档 - [English](https://redis.uptrace.dev) - [简体中文](https://redis.uptrace.dev/zh/) ## 生态系统 - [Entra ID (Azure AD)](https://github.com/redis/go-redis-entraid) - [分布式锁](https://github.com/bsm/redislock) - [Redis 缓存](https://github.com/go-redis/cache) - [速率限制](https://github.com/go-redis/redis_rate) ## 功能特性 - 除 QUIT 和 SYNC 外的所有 Redis 命令。 - 自动连接池。 - [StreamingCredentialsProvider(例如 entra id, oauth)](#1-streaming-credentials-provider-highest-priority)(实验性) - [Pub/Sub](https://redis.uptrace.dev/guide/go-redis-pubsub.html)。 - [Pipelines 和事务](https://redis.uptrace.dev/guide/go-redis-pipelines.html)。 - [脚本](https://redis.uptrace.dev/guide/lua-scripting.html)。 - [Redis Sentinel](https://redis.uptrace.dev/guide/go-redis-sentinel.html)。 - [Redis Cluster](https://redis.uptrace.dev/guide/go-redis-cluster.html)。 - [Redis 性能监控](https://redis.uptrace.dev/guide/redis-performance-monitoring.html)。 - [Redis 概率数据结构 [RedisStack]](https://redis.io/docs/data-types/probabilistic/) - [可自定义的读写缓冲区大小。](#custom-buffer-sizes) ## 安装 go-redis 支持最新的 2 个 Go 版本,并且需要支持 [modules](https://github.com/golang/go/wiki/Modules) 的 Go 版本。因此请确保初始化一个 Go module: ``` go mod init github.com/my/repo ``` 然后安装 go-redis/**v9**: ``` go get github.com/redis/go-redis/v9 ``` ## 快速开始 ``` import ( "context" "fmt" "github.com/redis/go-redis/v9" ) var ctx = context.Background() func ExampleClient() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB }) defer rdb.Close() err := rdb.Set(ctx, "key", "value", 0).Err() if err != nil { panic(err) } val, err := rdb.Get(ctx, "key").Result() if err != nil { panic(err) } fmt.Println("key", val) val2, err := rdb.Get(ctx, "key2").Result() if err == redis.Nil { fmt.Println("key2 does not exist") } else if err != nil { panic(err) } else { fmt.Println("key2", val2) } // Output: key value // key2 does not exist } ``` ### 身份验证 Redis 客户端支持多种提供身份验证凭据的方式,并具有明确的优先级顺序。以下是可用选项: #### 1. 流式凭据提供器(最高优先级)- 实验性功能 流式凭据提供器允许在连接生命周期内动态更新凭据。这对于托管身份服务和基于 token 的身份验证特别有用。 ``` type StreamingCredentialsProvider interface { Subscribe(listener CredentialsListener) (Credentials, UnsubscribeFunc, error) } type CredentialsListener interface { OnNext(credentials Credentials) // Called when credentials are updated OnError(err error) // Called when an error occurs } type Credentials interface { BasicAuth() (username string, password string) RawCredentials() string } ``` 使用示例: ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", StreamingCredentialsProvider: &MyCredentialsProvider{}, }) ``` **注意:** 流式凭据提供器可与 [go-redis-entraid](https://github.com/redis/go-redis-entraid) 配合使用,以启用 Entra ID(前身为 Azure AD)身份验证。这允许与 Azure 的托管身份服务和基于 token 的身份验证无缝集成。 Entra ID 示例: ``` import ( "github.com/redis/go-redis/v9" "github.com/redis/go-redis-entraid" ) // Create an Entra ID credentials provider provider := entraid.NewDefaultAzureIdentityProvider() // Configure Redis client with Entra ID authentication rdb := redis.NewClient(&redis.Options{ Addr: "your-redis-server.redis.cache.windows.net:6380", StreamingCredentialsProvider: provider, TLSConfig: &tls.Config{ MinVersion: tls.VersionTLS12, }, }) ``` #### 2. 基于上下文的凭据提供器 基于上下文的提供器允许使用 context 在每次操作时确定凭据。 ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", CredentialsProviderContext: func(ctx context.Context) (string, string, error) { // Return username, password, and any error return "user", "pass", nil }, }) ``` #### 3. 常规凭据提供器 一个简单的基于函数的提供器,返回静态凭据。 ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", CredentialsProvider: func() (string, string) { // Return username and password return "user", "pass" }, }) ``` #### 4. 用户名/密码字段(最低优先级) 提供凭据最基本的方式是通过选项中的 `Username` 和 `Password` 字段。 ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Username: "user", Password: "pass", }) ``` #### 优先级顺序 客户端将按以下优先级顺序使用凭据: 1. 流式凭据提供器(如果已设置) 2. 基于上下文的凭据提供器(如果已设置) 3. 常规凭据提供器(如果已设置) 4. 用户名/密码字段(如果已设置) 如果这些都未设置,客户端将尝试在未进行身份验证的情况下连接。 ### 协议版本 客户端同时支持 RESP2 和 RESP3 协议。您可以在选项中指定协议版本: ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB Protocol: 3, // specify 2 for RESP 2 or 3 for RESP 3 }) ``` ### 通过 redis url 连接 go-redis 也支持通过 [redis uri 规范](https://github.com/redis/redis-specifications/tree/master/uri/redis.txt) 进行连接。 下面的示例演示了如何使用字符串轻松配置连接,并遵循 此规范。 ``` import ( "github.com/redis/go-redis/v9" ) func ExampleClient() *redis.Client { url := "redis://user:password@localhost:6379/0?protocol=3" opts, err := redis.ParseURL(url) if err != nil { panic(err) } return redis.NewClient(opts) } ``` ### 使用 OpenTelemetry 进行检测 ``` import ( "github.com/redis/go-redis/v9" "github.com/redis/go-redis/extra/redisotel/v9" "errors" ) func main() { ... rdb := redis.NewClient(&redis.Options{...}) if err := errors.Join(redisotel.InstrumentTracing(rdb), redisotel.InstrumentMetrics(rdb)); err != nil { log.Fatal(err) } ``` ### 缓冲区大小配置 go-redis 默认使用 32KiB 的读写缓冲区以获得最佳性能。对于高吞吐量应用程序或大型 pipelines,您可以自定义缓冲区大小: ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", ReadBufferSize: 1024 * 1024, // 1MiB read buffer WriteBufferSize: 1024 * 1024, // 1MiB write buffer }) ``` ### 高级配置 go-redis 支持扩展客户端标识阶段,以允许项目发送其自己的自定义客户端标识。 #### 默认客户端标识 默认情况下,go-redis 在连接过程中自动发送客户端库名称和版本。此功能在 redis-server 7.2 版本中提供。因此,该命令是“即发即弃”的,这意味着如果 redis 服务器不支持此功能,它应该静默失败。 #### 禁用身份验证 当不需要连接身份验证或需要显式禁用时,存在一个 `DisableIdentity` 配置选项。 最初有一个拼写错误,该选项被命名为 `DisableIndentity` 而不是 `DisableIdentity`。拼写错误的选项被标记为已弃用,并将在本库的 V10 版本中删除。 尽管目前这两个选项都有效,但正确的选项是 `DisableIdentity`。已弃用的选项将在本库的 V10 版本中删除,因此请使用正确的选项名称以避免任何问题。 要禁用验证,请在 Redis 客户端选项中将 `DisableIdentity` 选项设置为 `true`: ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, DisableIdentity: true, // Disable set-info on connect }) ``` #### RediSearch 命令的不稳定 RESP3 结构 使用 RESP3 将 Redis 与应用程序功能集成时,请注意某些响应结构尚未最终确定。这对于搜索和查询结果等更复杂的结构尤为如此。我们建议在使用搜索和查询功能时使用 RESP2,但我们计划在接下来的版本中稳定基于 RESP3 的 API。您可以在即将发布的发行说明中找到更多指导。 要启用不稳定的 RESP3,请在客户端配置中设置该选项: ``` redis.NewClient(&redis.Options{ UnstableResp3: true, }) ``` **注意:** 启用 UnstableResp3 模式时,必须使用 RawResult() 和 RawVal() 来检索原始数据。 由于原始响应是不稳定搜索命令的唯一选择,因此 Val() 和 Result() 调用对它们不会有任何影响: ``` res1, err := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawResult() val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawVal() ``` #### Redis-Search 默认方言 在 Redis-Search 模块中,**默认方言为 2**。如果需要,您可以在查询中使用适当的配置显式指定不同的方言。 **重要**:请注意,查询方言可能会影响返回的结果。如果需要,您可以通过在要执行的命令参数中传递所需的方言来回退到不同的方言版本。 例如: ``` res2, err := rdb.FTSearchWithArgs(ctx, "idx:bicycle", "@pickup_zone:[CONTAINS $bike]", &redis.FTSearchOptions{ Params: map[string]interface{}{ "bike": "POINT(-0.1278 51.5074)", }, DialectVersion: 3, }, ).Result() ``` 您可以在[查询方言文档](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/)中找到更多详细信息。 #### 自定义缓冲区大小 在 v9.12 之前,缓冲区大小是 Go 的默认值 4096 字节。从 v9.12 开始, go-redis 默认使用 32KiB 的读写缓冲区以获得最佳性能。 对于高吞吐量应用程序或大型 pipelines,您可以自定义缓冲区大小: ``` rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", ReadBufferSize: 1024 * 1024, // 1MiB read buffer WriteBufferSize: 1024 * 1024, // 1MiB write buffer }) ``` **重要**:如果您在使用默认缓冲区大小时遇到任何问题,请尝试将其设置为 Go 的默认值 4096 字节。 ## 外观和感觉 一些边缘情况: ``` // SET key value EX 10 NX set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result() // SET key value keepttl NX set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result() // SORT list LIMIT 0 2 ASC vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() // ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ Min: "-inf", Max: "+inf", Offset: 0, Count: 2, }).Result() // ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{ Keys: []string{"zset1", "zset2"}, Weights: []int64{2, 3} }).Result() // EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() // custom command res, err := rdb.Do(ctx, "set", "key", "value").Result() ``` ## 类型化错误 go-redis 为常见的 Redis 错误提供了类型化错误检查函数: ``` // Cluster and replication errors redis.IsLoadingError(err) // Redis is loading the dataset redis.IsReadOnlyError(err) // Write to read-only replica redis.IsClusterDownError(err) // Cluster is down redis.IsTryAgainError(err) // Command should be retried redis.IsMasterDownError(err) // Master is down redis.IsMovedError(err) // Returns (address, true) if key moved redis.IsAskError(err) // Returns (address, true) if key being migrated // Connection and resource errors redis.IsMaxClientsError(err) // Maximum clients reached redis.IsAuthError(err) // Authentication failed (NOAUTH, WRONGPASS, unauthenticated) redis.IsPermissionError(err) // Permission denied (NOPERM) redis.IsOOMError(err) // Out of memory (OOM) // Transaction errors redis.IsExecAbortError(err) // Transaction aborted (EXECABORT) ``` ### Hook 中的错误包装 在 hook 中包装错误时,请使用带有 `Unwrap()` 方法的自定义错误类型(首选)或使用带有 `%w` 的 `fmt.Errorf`。务必调用 `cmd.SetErr()` 以保留错误类型信息: ``` // Custom error type (preferred) type AppError struct { Code string RequestID string Err error } func (e *AppError) Error() string { return fmt.Sprintf("[%s] request_id=%s: %v", e.Code, e.RequestID, e.Err) } func (e *AppError) Unwrap() error { return e.Err } // Hook implementation func (h MyHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook { return func(ctx context.Context, cmd redis.Cmder) error { err := next(ctx, cmd) if err != nil { // Wrap with custom error type wrappedErr := &AppError{ Code: "REDIS_ERROR", RequestID: getRequestID(ctx), Err: err, } cmd.SetErr(wrappedErr) return wrappedErr // Return wrapped error to preserve it } return nil } } // Typed error detection works through wrappers if redis.IsLoadingError(err) { // Retry logic } // Extract custom error if needed var appErr *AppError if errors.As(err, &appErr) { log.Printf("Request: %s", appErr.RequestID) } ``` 或者,使用带有 `%w` 的 `fmt.Errorf`: ``` wrappedErr := fmt.Errorf("context: %w", err) cmd.SetErr(wrappedErr) ``` ### Pipeline Hook 示例 对于 pipeline 操作,请使用 `ProcessPipelineHook`: ``` type PipelineLoggingHook struct{} func (h PipelineLoggingHook) DialHook(next redis.DialHook) redis.DialHook { return next } func (h PipelineLoggingHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook { return next } func (h PipelineLoggingHook) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook { return func(ctx context.Context, cmds []redis.Cmder) error { start := time.Now() // Execute the pipeline err := next(ctx, cmds) duration := time.Since(start) log.Printf("Pipeline executed %d commands in %v", len(cmds), duration) // Process individual command errors // Note: Individual command errors are already set on each cmd by the pipeline execution for _, cmd := range cmds { if cmdErr := cmd.Err(); cmdErr != nil { // Check for specific error types using typed error functions if redis.IsAuthError(cmdErr) { log.Printf("Auth error in pipeline command %s: %v", cmd.Name(), cmdErr) } else if redis.IsPermissionError(cmdErr) { log.Printf("Permission error in pipeline command %s: %v", cmd.Name(), cmdErr) } // Optionally wrap individual command errors to add context // The wrapped error preserves type information through errors.As() wrappedErr := fmt.Errorf("pipeline cmd %s failed: %w", cmd.Name(), cmdErr) cmd.SetErr(wrappedErr) } } // Return the pipeline-level error (connection errors, etc.) // You can wrap it if needed, or return it as-is return err } } // Register the hook rdb.AddHook(PipelineLoggingHook{}) // Use pipeline - errors are still properly typed pipe := rdb.Pipeline() pipe.Set(ctx, "key1", "value1", 0) pipe.Get(ctx, "key2") _, err := pipe.Exec(ctx) ``` ## 运行测试 建议使用 Docker,只需运行: ``` make test ``` ## 另请参阅 - [Golang ORM](https://bun.uptrace.dev),适用于 PostgreSQL、MySQL、MSSQL 和 SQLite - [Golang PostgreSQL](https://bun.uptrace.dev/postgres/) - [Golang HTTP 路由](https://bunrouter.uptrace.dev/) - [Golang ClickHouse ORM](https://github.com/uptrace/go-clickhouse) ## 贡献者 感谢所有已经做出贡献的人!
标签:EVTX分析, Golang包, go-redis, Go语言, Redis 8, Redis客户端, 中间件, 后端开发, 存储组件, 开源库, 搜索引擎查询, 搜索引擎爬虫, 数据库连接器, 数据结构, 日志审计, 用户代理, 程序破解, 键值存储