Top 10 Redis Use Cases with Go: Practical Guide
This guide walks through ten classic Redis scenarios—caching, session storage, rate limiting, leaderboards, message queues, Pub/Sub, real‑time analytics, distributed locks, geospatial queries, and shopping carts—providing Go code examples, best‑practice tips, and performance considerations for each pattern.
Environment Setup
Install the Go Redis client and start Redis with Docker.
go get github.com/redis/go-redis/v9
docker run -d -p 6379:6379 redis:latest1️⃣ Caching (Cache‑Aside)
Store frequently accessed data in Redis strings with a TTL to reduce database load.
func getUserWithCache(userID string) (*User, error) {
key := fmt.Sprintf("user:%s", userID)
data, err := rdb.Get(ctx, key).Result()
if err == nil {
var user User
json.Unmarshal([]byte(data), &user)
return &user, nil
}
user, err := getUserFromDB(userID)
if err != nil {
return nil, err
}
userData, _ := json.Marshal(user)
rdb.Set(ctx, key, userData, 30*time.Minute).Err()
return user, nil
}💡 Set a reasonable TTL (5‑30 minutes) to avoid stale data.
2️⃣ Session Store
Stateless session management using Redis strings or hashes.
type Session struct {
UserID string `json:"user_id"`
Username string `json:"username"`
LoginAt time.Time `json:"login_at"`
ExpiresAt time.Time `json:"expires_at"`
}
func createSession(userID, username string) (string, error) {
sessionID := generateUUID()
session := &Session{
UserID: userID,
Username: username,
LoginAt: time.Now(),
ExpiresAt: time.Now().Add(24 * time.Hour),
}
key := fmt.Sprintf("session:%s", sessionID)
data, _ := json.Marshal(session)
err := rdb.Set(ctx, key, data, 24*time.Hour).Err()
return sessionID, err
}3️⃣ Rate Limiting
Protect APIs with a fixed‑window counter or a sliding‑window (token bucket) algorithm.
// Fixed‑window
func rateLimit(userID string, limit int, window time.Duration) bool {
key := fmt.Sprintf("ratelimit:%s", userID)
count, _ := rdb.Incr(ctx, key).Result()
if count == 1 {
rdb.Expire(ctx, key, window)
}
return count <= int64(limit)
}
// Sliding‑window (token bucket)
func slidingWindowRateLimit(userID string, limit int, window time.Duration) bool {
key := fmt.Sprintf("ratelimit:sliding:%s", userID)
now := time.Now().UnixNano()
start := now - window.Nanoseconds()
rdb.ZRemRangeByScore(ctx, key, "0", fmt.Sprintf("%d", start))
rdb.ZAdd(ctx, key, &redis.Z{Score: float64(now), Member: fmt.Sprintf("%d", now)})
rdb.Expire(ctx, key, window)
cnt, _ := rdb.ZCard(ctx, key).Result()
return cnt <= int64(limit)
}⚠️ Choose fixed‑window for simplicity; sliding‑window provides finer granularity.
4️⃣ Leaderboard
Maintain real‑time rankings with a sorted set.
func addPlayerScore(playerID string, score int64) error {
return rdb.ZAdd(ctx, "leaderboard:global", &redis.Z{Score: float64(score), Member: playerID}).Err()
}
func getPlayerRank(playerID string) (int64, error) {
rank, err := rdb.ZRevRank(ctx, "leaderboard:global", playerID).Result()
if err != nil {
return 0, err
}
return rank + 1, nil // 1‑based rank
}5️⃣ Message Queue
Lightweight queue using a Redis list.
func enqueue(queueName string, message interface{}) error {
data, _ := json.Marshal(message)
return rdb.RPush(ctx, fmt.Sprintf("queue:%s", queueName), data).Err()
}
func dequeue(queueName string, timeout time.Duration) ([]byte, error) {
result, err := rdb.BLPop(ctx, timeout, fmt.Sprintf("queue:%s", queueName)).Result()
if err != nil {
return nil, err
}
return []byte(result[1]), nil
}6️⃣ Pub/Sub
Real‑time message broadcasting.
func publish(channel string, message interface{}) error {
data, _ := json.Marshal(message)
return rdb.Publish(ctx, channel, data).Err()
}
func subscribe(channels ...string) *redis.PubSub {
ps := rdb.Subscribe(ctx, channels...)
_, err := ps.Receive(ctx)
if err != nil {
panic(err)
}
return ps
}
func receiveMessage(ps *redis.PubSub) (*redis.Message, error) {
return ps.ReceiveMessage(ctx)
}⚠️ Pub/Sub messages are not persisted; offline subscribers lose messages.
7️⃣ Real‑time Analytics
Track page views (PV) and unique visitors (UV) using counters and HyperLogLog.
func recordPageView(pageID, userID string) error {
key := fmt.Sprintf("analytics:page:%s", pageID)
rdb.PFAdd(ctx, key, userID) // UV
rdb.Incr(ctx, fmt.Sprintf("analytics:pv:%s", pageID)) // PV
return rdb.Expire(ctx, key, 7*24*time.Hour).Err()
}
func getUniqueVisitors(pageID string) (int64, error) {
return rdb.PFCount(ctx, fmt.Sprintf("analytics:page:%s", pageID)).Result()
}
func getPageViews(pageID string) (int64, error) {
return rdb.Get(ctx, fmt.Sprintf("analytics:pv:%s", pageID)).Int64()
}8️⃣ Distributed Lock
Ensure exclusive access across multiple instances.
func acquireLock(lockKey, lockID string, ttl time.Duration) bool {
result, err := rdb.Set(ctx, lockKey, lockID, &redis.SetArgs{NX: true, EX: int64(ttl.Seconds())}).Result()
return err == nil && result == "OK"
}
func releaseLock(lockKey, lockID string) bool {
lua := `if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end`
n, _ := rdb.Eval(ctx, lua, []string{lockKey}, lockID).Int64()
return n == 1
}⚠️ Verify lock ID on release to avoid deleting another client’s lock.
9️⃣ Geospatial
Store and query locations for O2O services.
type Location struct {
Name string
Longitude float64
Latitude float64
}
func addLocation(key string, loc Location) error {
return rdb.GeoAdd(ctx, key, &redis.GeoLocation{
Name: loc.Name,
Longitude: loc.Longitude,
Latitude: loc.Latitude,
}).Err()
}
func findNearby(key string, lon, lat, radius float64) ([]redis.GeoLocation, error) {
return rdb.GeoRadius(ctx, key, lon, lat, &redis.GeoRadiusQuery{
Radius: radius,
Unit: "km",
WithDist: true,
}).Result()
}
func getDistance(key, loc1, loc2 string) (float64, error) {
return rdb.GeoDist(ctx, key, loc1, loc2, "km").Result()
}🔟 Shopping Cart
Implement an e‑commerce cart using a hash; each product is stored as JSON and its quantity in a separate field.
type CartItem struct {
ProductID string `json:"product_id"`
Name string `json:"name"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
}
func addToCart(userID, productID string, quantity int) error {
key := fmt.Sprintf("cart:%s", userID)
exists, _ := rdb.HExists(ctx, key, productID).Result()
if exists {
return rdb.HIncrBy(ctx, key, fmt.Sprintf("%s:qty", productID), int64(quantity)).Err()
}
product := getProduct(productID) // fetch from DB
data, _ := json.Marshal(product)
if err := rdb.HSet(ctx, key, productID, string(data)).Err(); err != nil {
return err
}
return rdb.HSet(ctx, key, fmt.Sprintf("%s:qty", productID), quantity).Err()
}
func getCart(userID string) ([]CartItem, error) {
key := fmt.Sprintf("cart:%s", userID)
data, _ := rdb.HGetAll(ctx, key).Result()
var items []CartItem
for field, jsonData := range data {
if strings.HasSuffix(field, ":qty") {
continue
}
var item CartItem
json.Unmarshal([]byte(jsonData), &item)
qty, _ := rdb.HGet(ctx, key, fmt.Sprintf("%s:qty", field)).Int()
item.Quantity = qty
items = append(items, item)
}
return items, nil
}Best Practices
// Connection pool
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
PoolSize: 100,
MinIdleConns: 10,
MaxRetries: 3,
PoolTimeout: 30 * time.Second,
})
// Health check
func checkRedisHealth() bool {
_, err := rdb.Ping(ctx).Result()
return err == nil
}
// Graceful shutdown
defer rdb.Close()Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Golang Shines
We share daily the latest Golang technical articles, practical resources, language news, tutorials, and real-world projects to help everyone learn and improve.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
