go-redis
GitHub Overview
Topics
Star History
Cache Library
go-redis
Overview
go-redis (redis/go-redis) is a Redis client library for Go. It provides high performance and rich functionality, enabling connections to Redis servers, data operations, and utilization of advanced Redis features. It supports Redis Cluster, Sentinel, various Redis modules, and offers OpenTelemetry instrumentation capabilities.
Details
go-redis supports all Redis functionality from basic key-value operations to sorted sets, hashes, lists, streams, and Pub/Sub. It supports both RESP2 and RESP3 protocols and includes built-in connection pooling, automatic reconnection, and failover capabilities.
It also provides extended functionality such as OpenTelemetry tracing and metrics, Prometheus metrics, and Redis Search. The library has comprehensive context.Context support, enabling proper timeout and cancellation handling, making it suitable for enterprise-level applications.
Pros and Cons
Pros
- Complete Redis feature support (Cluster, Sentinel, Modules, etc.)
- High-performance connection pooling and concurrent processing
- RESP2/RESP3 protocol support
- Comprehensive OpenTelemetry instrumentation
- Full context.Context support
- Automatic reconnection and failover capabilities
- Rich Redis extension support (Search, Bloom, etc.)
- Enterprise-level reliability
Cons
- Requires Redis server (infrastructure dependency)
- May be overkill for simple in-memory caching
- Requires learning Redis-specific concepts (data types, commands, etc.)
- Slower than pure in-memory cache due to network overhead
- Requires Redis configuration and tuning knowledge
Reference Links
Code Examples
Basic Setup
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
// Create basic Redis client
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password
DB: 0, // default DB
})
// Test connection
pong, err := rdb.Ping(ctx).Result()
if err != nil {
panic(err)
}
fmt.Println("Connected to Redis:", pong)
}
Basic Set/Get Operations
// Store string
err := rdb.Set(ctx, "user:name", "John Doe", 0).Err()
if err != nil {
panic(err)
}
// Get value
val, err := rdb.Get(ctx, "user:name").Result()
if err == redis.Nil {
fmt.Println("key does not exist")
} else if err != nil {
panic(err)
} else {
fmt.Println("user:name", val)
}
// Store with expiration
err = rdb.Set(ctx, "session:123", "active", 30*time.Minute).Err()
if err != nil {
panic(err)
}
URL Connection and Configuration Options
// Connect using URL
url := "redis://user:password@localhost:6379/0?protocol=3"
opts, err := redis.ParseURL(url)
if err != nil {
panic(err)
}
rdb := redis.NewClient(opts)
// Advanced configuration options
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "mypassword",
DB: 0,
PoolSize: 10, // Connection pool size
MinIdleConns: 5, // Minimum idle connections
MaxIdleTime: 5 * time.Minute, // Idle timeout
DialTimeout: 5 * time.Second, // Connection timeout
ReadTimeout: 3 * time.Second, // Read timeout
WriteTimeout: 3 * time.Second, // Write timeout
Protocol: 3, // Use RESP3 protocol
})
Advanced Redis Operations
// Hash operations
err := rdb.HSet(ctx, "user:123", map[string]interface{}{
"name": "John Doe",
"email": "[email protected]",
"age": 30,
}).Err()
userInfo := rdb.HGetAll(ctx, "user:123").Val()
fmt.Printf("User info: %v\n", userInfo)
// List operations
rdb.LPush(ctx, "tasks", "task1", "task2", "task3")
tasks := rdb.LRange(ctx, "tasks", 0, -1).Val()
fmt.Printf("Tasks: %v\n", tasks)
// Sorted set operations
rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "player1"})
rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 85, Member: "player2"})
// Get top 10
topPlayers := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 9).Val()
for _, player := range topPlayers {
fmt.Printf("Player: %s, Score: %.0f\n", player.Member, player.Score)
}
Pipeline Processing
// Batch processing using pipeline
pipe := rdb.Pipeline()
incr := pipe.Incr(ctx, "counter")
pipe.Expire(ctx, "counter", time.Hour)
// Execute pipeline
_, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
// Get results
fmt.Println("Counter value:", incr.Val())
Transactions (MULTI/EXEC)
// Execute transaction
err := rdb.Watch(ctx, func(tx *redis.Tx) error {
// Get current value
currentVal := tx.Get(ctx, "balance").Val()
balance, _ := strconv.Atoi(currentVal)
if balance < 100 {
return fmt.Errorf("insufficient balance")
}
// Start transaction
_, err := tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.Decr(ctx, "balance")
pipe.Incr(ctx, "spent")
return nil
})
return err
}, "balance")
if err != nil {
fmt.Printf("Transaction failed: %v\n", err)
}
Pub/Sub Functionality
// Publisher
func publisher(rdb *redis.Client) {
for i := 0; i < 10; i++ {
message := fmt.Sprintf("Message %d", i)
err := rdb.Publish(ctx, "notifications", message).Err()
if err != nil {
panic(err)
}
time.Sleep(1 * time.Second)
}
}
// Subscriber
func subscriber(rdb *redis.Client) {
pubsub := rdb.Subscribe(ctx, "notifications")
defer pubsub.Close()
ch := pubsub.Channel()
for msg := range ch {
fmt.Printf("Received: %s\n", msg.Payload)
}
}
Redis Cluster Connection
// Cluster client
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{
"localhost:7000",
"localhost:7001",
"localhost:7002",
},
Password: "cluster-password",
})
// Use same API as regular operations
err := rdb.Set(ctx, "cluster-key", "cluster-value", 0).Err()
if err != nil {
panic(err)
}
OpenTelemetry Integration
import (
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/extra/redisotel/v9"
)
// OpenTelemetry instrumentation
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// Enable tracing and metrics
err := redisotel.InstrumentTracing(rdb)
if err != nil {
panic(err)
}
err = redisotel.InstrumentMetrics(rdb)
if err != nil {
panic(err)
}
Error Handling and Retry
// Custom retry configuration
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
MaxRetries: 3,
MinRetryBackoff: 8 * time.Millisecond,
MaxRetryBackoff: 512 * time.Millisecond,
})
// Error handling
val, err := rdb.Get(ctx, "nonexistent").Result()
switch {
case err == redis.Nil:
fmt.Println("Key does not exist")
case err != nil:
fmt.Printf("Error occurred: %v\n", err)
default:
fmt.Printf("Value: %s\n", val)
}