Cache em Go: Redis, Memcached e soluções embutidas: do A ao Z
\n\nCache é uma das ferramentas-chave para melhorar o desempenho de aplicações web e microsserviços. Em Go (Golang), existem várias abordagens: desde simples mapas in-memory até sistemas distribuídos poderosos como Redis e Memcached. Neste artigo, abordaremos todas as opções populares de cache em Go com exemplos de código, compararemos seu desempenho e mostraremos as melhores práticas de implementação.
\n\nPor que o cache é necessário em Go?
\nSem cache, cada acesso a dados pode levar a operações custosas: consultas ao banco de dados, chamadas a APIs externas ou cálculos complexos. O cache permite salvar o resultado da primeira operação e entregá-lo rapidamente em requisições repetidas. Isso reduz a carga no backend, diminui a latência e melhora a experiência do usuário. Em Go, devido às goroutines e alta concorrência, a implementação correta do cache é especialmente importante.
\n\n1. Soluções embutidas: sync.Map e bigcache
\nPara projetos pequenos ou casos onde não é necessária distribuição, o Go oferece mecanismos de cache embutidos.
\n\n1.1 sync.Map
\nsync.Map é um mapa thread-safe da biblioteca padrão do Go. É otimizado para casos onde as chaves são escritas uma vez e lidas muitas vezes. Ideal para caches com baixa frequência de atualizações.
package main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc main() {\n var cache sync.Map\n\n // Escrita\n cache.Store("user:1", "Alice")\n cache.Store("user:2", "Bob")\n\n // Leitura\n if value, ok := cache.Load("user:1"); ok {\n fmt.Println("Encontrado:", value)\n }\n\n // Remoção\n cache.Delete("user:2")\n\n // Iteração (Range)\n cache.Range(func(key, value interface{}) bool {\n fmt.Printf("Chave: %v, Valor: %v\\n", key, value)\n return true\n })\n}\nVantagens: sem dependências externas, API simples, thread-safe.
\nDesvantagens: sem TTL (tempo de vida), sem limite de tamanho, desempenho reduzido com escritas frequentes.
1.2 BigCache
\nBigCache é uma biblioteca de cache in-memory rápida, especialmente projetada para Go. Minimiza alocações de memória e suporta TTL.
\npackage main\n\nimport (\n "fmt"\n "time"\n "github.com/allegro/bigcache/v3"\n)\n\nfunc main() {\n config := bigcache.Config{\n Shards: 1024,\n LifeWindow: 10 * time.Minute,\n CleanWindow: 5 * time.Minute,\n MaxEntriesInWindow: 1000,\n MaxEntrySize: 500,\n Verbose: true,\n }\n\n cache, err := bigcache.NewBigCache(config)\n if err != nil {\n panic(err)\n }\n\n // Escrita\n cache.Set("session:abc123", []byte("user_data"))\n\n // Leitura\n data, err := cache.Get("session:abc123")\n if err == nil {\n fmt.Println("Dados:", string(data))\n }\n}\nVantagens: alta velocidade, suporte a TTL, baixa carga no GC (Garbage Collector).
\nDesvantagens: apenas para uma única instância da aplicação, sem replicação.
2. Memcached em Go
\nMemcached é um sistema de cache distribuído em memória. É simples, mas muito rápido e suporta TTL. Em Go, para trabalhar com Memcached, usa-se o driver github.com/bradfitz/gomemcache/memcache.
package main\n\nimport (\n "fmt"\n "github.com/bradfitz/gomemcache/memcache"\n)\n\nfunc main() {\n mc := memcache.New("127.0.0.1:11211")\n\n // Escrita\n err := mc.Set(&memcache.Item{\n Key: "user:42",\n Value: []byte("John Doe"),\n Expiration: 300, // 5 minutos\n })\n if err != nil {\n panic(err)\n }\n\n // Leitura\n item, err := mc.Get("user:42")\n if err == nil {\n fmt.Println("Valor:", string(item.Value))\n }\n\n // Rem