Redis
go-zero wraps the Redis client with connection pooling, automatic retries, metrics, and tracing. The core/stores/redis package exposes a clean API covering strings, hashes, sets, sorted sets, lists, and distributed locks.
Configuration
Section titled “Configuration”Redis: Host: 127.0.0.1:6379 Pass: "" Type: node # "node" (single) | "cluster" (Redis Cluster)For Redis Cluster:
Redis: Host: redis-master:6379 # any cluster node; client discovers the rest Type: cluster Pass: ""Initialize
Section titled “Initialize”import "github.com/zeromicro/go-zero/core/stores/redis"
func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Redis: redis.MustNewRedis(c.Redis), }}String Operations
Section titled “String Operations”// Set with TTL (seconds)err := rdb.Setex("session:abc123", "userId:42", 3600)
// Getval, err := rdb.Get("session:abc123")
// Set if not exists (SETNX) — used for distributed deduplicationok, err := rdb.SetnxEx("idempotency:order:X9", "1", 86400)if !ok { return nil, ErrDuplicateRequest}
// Atomic increment (counter)count, err := rdb.Incr("stats:page:views")
// Increment by a specific amountcount, err = rdb.IncrBy("stats:total:sales", 150)Distributed Lock
Section titled “Distributed Lock”lock := redis.NewRedisLock(rdb, "order:process:12345")lock.SetExpire(5) // 5-second lock TTL
acquired, err := lock.Acquire()if err != nil { return nil, err}if !acquired { return nil, ErrLockNotAcquired}defer lock.Release()
// --- critical section ---Hash Operations
Section titled “Hash Operations”// Set fields on a hasherr = rdb.Hset("user:42", "name", "Alice")
// Get a fieldname, err := rdb.Hget("user:42", "name")
// Get all fieldsfields, err := rdb.Hgetall("user:42")
// Increment a hash field (e.g., for per-user counters)val, err := rdb.Hincrby("user:42", "loginCount", 1)Sorted Set (Leaderboard, Rate Window)
Section titled “Sorted Set (Leaderboard, Rate Window)”// Add / update scoreerr = rdb.Zadd("leaderboard", 1500, "player:alice")
// Get rank (0-based, ascending)rank, err := rdb.Zrank("leaderboard", "player:alice")
// Get top-10 (descending)members, err := rdb.ZrevrangeWithScores("leaderboard", 0, 9)
// Sliding-window rate limiting: add timestamp as score, trim old entriesnow := time.Now().UnixMilli()windowStart := now - int64(60*1000) // last 60 s_ = rdb.Zadd("rl:user:42", float64(now), fmt.Sprintf("%d", now))_ = rdb.Zremrangebyscore("rl:user:42", "0", strconv.FormatInt(windowStart, 10))count, _ := rdb.Zcard("rl:user:42")if count > 100 { return nil, ErrRateLimited}Pipeline
Section titled “Pipeline”Send multiple commands in a single round-trip:
_, err = rdb.Pipelined(func(pipe redis.Pipeliner) error { pipe.Set(ctx, "k1", "v1", time.Minute) pipe.Set(ctx, "k2", "v2", time.Minute) pipe.Incr(ctx, "counter") return nil})Pub / Sub
Section titled “Pub / Sub”// Publisher_ = rdb.Publish("events:order", `{"id":42,"status":"paid"}`)
// Subscriber (typically in a background goroutine)pubsub, err := rdb.Subscribe("events:order")if err != nil { log.Fatal(err)}defer pubsub.Close()
for msg := range pubsub.Channel() { fmt.Println(msg.Payload)}TTL Management
Section titled “TTL Management”// Check remaining TTLttl, err := rdb.Ttl("session:abc123") // returns time.Duration
// Refresh TTL on access_ = rdb.Expire("session:abc123", 3600)
// Remove TTL (persist the key)_ = rdb.Persist("session:abc123")Cache-Aside with collection.Cache (L1)
Section titled “Cache-Aside with collection.Cache (L1)”For hot data, combine Redis with an in-process L1 cache to avoid Redis round-trips on every request:
var product Producterr = cache.TakeCtx(ctx, &product, fmt.Sprintf("product:%d", id), func(v any) error { *v.(*Product) = fetchFromDB(id) return nil})See Memory Cache for the full L1/L2 pattern.
Connection Health
Section titled “Connection Health”// Ping checks connectivity (use in health-check handlers)if err := rdb.Ping(); err != nil { httpx.Error(w, err, http.StatusServiceUnavailable) return}