Caching in Go: Redis, Memcached, and Built-in Solutions
Caching is one of the key tools for improving the performance of web applications and services. In the Go (Golang) ecosystem, developers have access to both external caching systems (Redis, Memcached) and built-in solutions like sync.Map or the bigcache and freecache libraries. In this article, we will analyze the pros and cons of each approach, provide code examples, and help you choose the optimal tool for your project.
1. Built-in Caching: sync.Map and map with Mutex
Go offers basic primitives for creating a cache directly in the application's memory. The simplest way is to use a regular map with a mutex for thread safety.
Example: Simple Thread-Safe Cache
package main
import ( "fmt" "sync" "time")
type Cache struct { mu sync.RWMutex items map[string]interface{}}
func NewCache() *Cache { return &Cache{ items: make(map[string]interface{}), }}
func (c *Cache) Set(key string, value interface{}) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = value}
func (c *Cache) Get(key string) (interface{}, bool) { c.mu.RLock() defer c.mu.RUnlock() val, ok := c.items[key] return val, ok}
func main() { cache := NewCache() cache.Set("user:1", "Alice") val, _ := cache.Get("user:1") fmt.Println(val) // Alice}For more complex scenarios, Go provides sync.Map — an optimized concurrent map that is better suited for cases where keys are written once and read many times.
When to Use Built-in Caching?
- Micro-caching (for the duration of a single request or session).
- Singleton applications without a distributed architecture.
- Simplicity — no need to set up external services.
Cons: lack of automatic invalidation, limited size (depends on application memory), no persistence.
2. BigCache and FreeCache: Lightweight In-Memory Solutions
The bigcache and freecache libraries solve the problem of limited memory and GC (garbage collection). They use pre-allocated buffers and minimize the load on the Go garbage collector.
Example with BigCache
package main
import ( "fmt" "github.com/allegro/bigcache/v3" "time")
func main() { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute)) cache.Set("mykey", []byte("myvalue")) val, _ := cache.Get("mykey") fmt.Println(string(val)) // myvalue}Example with FreeCache
package main
import ( "fmt" "github.com/coocood/freecache")
func main() { cache := freecache.NewCache(100 * 1024) // 100 KB cache.Set([]byte("key"), []byte("value"), 60) // TTL 60 sec val, _ := cache.Get([]byte("key")) fmt.Println(string(val)) // value}Both libraries support TTL (time to live) and automatic deletion of old entries. BigCache is better for large data volumes (hundreds of gigabytes), FreeCache is better for scenarios with frequent read/write operations.
3. Redis: Distributed Caching for Production
Redis is the most popular choice for caching in Go projects. It runs as a separate server, supports complex data structures, persistence, and clustering.
Connecting Redis in Go
package main
import ( "context" "fmt" "github.com/redis/go-redis/v9")
func main() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: