Caching in Go: Redis, Memcached, and Built-in Solutions
Caching is one of the key tools for improving the performance of web applications and microservices. In Go (Golang), there are several approaches: from simple built-in data structures to powerful distributed systems like Redis and Memcached. In this article, we will cover the main caching options, their advantages, disadvantages, and provide practical code examples.
Why is caching needed in Go?
Go is renowned for its speed and efficiency, but even in Go, bottlenecks often occur: slow database queries, external APIs, or heavy computations. Caching allows you to:
- Reduce the load on the database and external services.
- Decrease API response time (latency).
- Save server resources (CPU, memory).
- Increase fault tolerance during temporary failures.
The choice of a specific solution depends on the application architecture: monolith or microservices, data consistency requirements, cache size, and budget.
Built-in Caching in Go
For simple scenarios (e.g., caching data within a single process), Go's built-in tools are suitable. They do not require installing external services and are ideal for small applications.
1. sync.Map
sync.Map is a thread-safe map from the Go standard library. It is suitable for caching that is rarely updated and frequently read. Example:
package main
import ( "fmt" "sync")
func main() { var cache sync.Map
// Store data cache.Store("user_1", "Alice") cache.Store("user_2", "Bob")
// Read data if val, ok := cache.Load("user_1"); ok { fmt.Println("Found:", val) }
// Delete cache.Delete("user_2")}Pros: simplicity, no external dependencies, thread safety. Cons: no TTL (time to live), no automatic cleanup, limited to a single process.
2. go-cache package
The popular library github.com/patrickmn/go-cache adds support for TTL and automatic cleanup. Example:
package main
import ( "fmt" "time" "github.com/patrickmn/go-cache")
func main() { // Create a cache with a 5-minute default expiration and 10-minute cleanup interval c := cache.New(5*time.Minute, 10*time.Minute)
// Store with TTL c.Set("session_token", "abc123", cache.DefaultExpiration)
// Retrieve token, found := c.Get("session_token") if found { fmt.Println("Token:", token) }
// Delete c.Delete("session_token")}Pros: TTL, ease of integration, automatic cleanup. Cons: data is stored in the memory of a single process, not suitable for distributed systems.
Redis: Distributed Caching for Go
Redis is the most popular caching solution for Go. It runs as a separate server, supports complex data structures (strings, hashes, lists, sets), and has excellent support in Go via the go-redis driver.
Connection and Basic Operations
package main
import ( "context" "fmt" "github.com/redis/go-redis/v9")
func main() { ctx := context.Background()
// Connect to Redis rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password DB: 0, })
// Store a string with TTL (1 hour) err := rdb.Set(ctx, "key", "value", 3600*time.Second).Err() if err != nil { panic(err) }
// Read val, err := rdb.Get(ctx, "key").Result() if err != nil { fmt.Println("Key not found") } else { fmt.Println("Value:", val) }
// Delete rdb.Del(ctx, "key")}