Cache em Go: como acelerar sua aplicação em 100 vezes sem enlouquecer

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que sua aplicação Go está lenta? Provavelmente você esqueceu o cache



Você escreveu a aplicação perfeita em Go. Se orgulha das goroutines, canais e do compilador rápido. Mas quando a carga atinge 1000 RPS, tudo começa a ranger. O banco de dados cai, a API responde em 5 segundos, os usuários vão para os concorrentes.



Situação familiar? Bem-vindo ao mundo onde cache em Go é indispensável. Neste artigo, vamos ver como transformar sua aplicação tartaruga em um guepardo. Sem enrolação, com exemplos reais e soluções prontas.



A propósito, meu nome é Python, e este é o PythonLib. Sim, eu sei que estamos falando de Go. Mas código bom não conhece fronteiras de linguagens, e escrevemos artigos úteis sobre tudo. Vamos nessa!



O que é cache e por que você precisa dele em Go?



Cache de dados é o armazenamento de dados frequentemente solicitados em um repositório rápido (memória RAM, Redis, arquivos) para não consultar a fonte lenta (banco de dados, API externa, sistema de arquivos) toda vez.



Go é uma linguagem para sistemas de alta carga. Mas mesmo em Go você pode escrever código lento se não usar cache. Imagine: você consulta a lista de países do PostgreSQL 1000 vezes por segundo. Em vez de 1000 consultas ao banco, você pode fazer apenas 1, e as outras 999 vezes entregar os dados do cache in-memory Go.



Resultado: o tempo de resposta cai de 50 ms para 0.5 ms. Uma diferença de 100 vezes. Isso não é mágica, é arquitetura bem planejada.



Quando o cache é obrigatório?

  • Alta carga (1000+ requisições por segundo)
  • APIs externas lentas (pagamentos, clima, taxas de câmbio)
  • Cálculos pesados (relatórios, análises)
  • Dados de referência frequentemente solicitados (listas de cidades, categorias de produtos)


Estratégias de cache: escolhendo a correta



Para não transformar o cache em antipadrão, você precisa escolher a estratégia de cache correta. Aqui estão as principais opções que funcionam em Go:



1. Cache-Aside (cache preguiçoso)

O padrão mais popular. A aplicação primeiro verifica o cache. Se os dados não estiverem lá (cache miss) — vai ao banco, salva o resultado no cache e retorna ao cliente.



func GetUser(cache *redis.Client, db *sql.DB, userID int) (*User, error) {    // Tenta obter do cache    val, err := cache.Get(ctx, "user:"+strconv.Itoa(userID)).Result()    if err == nil {        user := &User{}        json.Unmarshal([]byte(val), user)        return user, nil    }

// Cache miss — vai ao banco user, err := db.Query("SELECT * FROM users WHERE id = ?", userID) if err != nil { return nil, err }

// Salva no cache por 1 hora data, _ := json.Marshal(user) cache.Set(ctx, "user:"+strconv.Itoa(userID), data, time.Hour)

return user, nil}


2. Write-Through (escura direta)

Os dados são escritos simultaneamente no cache e no banco. Vantagem: o cache está sempre atualizado. Desvantagem: a escrita é um pouco mais lenta.



3. TTL (Time To Live)

A maneira mais simples de invalidação de cache. Os dados vivem por um tempo fixo e depois são removidos automaticamente.



// Cache por 5 minutoscache.Set(ctx, "key", value, 5*time.Minute)


Implementação de cache in-memory em Go do zero



Às vezes você não quer subir um Redis só para algumas chaves. Para esses casos, o cache in-memory Go é perfeito. Vamos escrever uma versão simples, mas funcional:



package cache

import ( "sync" "time")

type Item struct { Value interface{} Expiration int64}

type Cache struct { items map[string]Item mu sync.RWMutex}

func New() *Cache { c := &Cache{ items: make(map[string]Item), } // Inicia goroutine para limpar entradas expiradas go c.cleanup() return c}

func (c *Cache) Set(key string, value interface{}, ttl time.Duration) { c.mu.Lock() defer c.mu.Un

Blogs

Book Recommendations