Caché en Go (Golang): Redis y otras soluciones
El caché es una de las herramientas clave para mejorar el rendimiento de las aplicaciones Go. Permite reducir el tiempo de respuesta, disminuir la carga en bases de datos y APIs externas. En este artículo, exploraremos los principales enfoques de caché en Go: el uso de Redis como almacenamiento externo, el caché integrado en memoria y el caché grupal con la biblioteca groupcache.
1. ¿Por qué es necesario el caché en Go?
Go es un lenguaje orientado a alto rendimiento, pero incluso las consultas más rápidas a la base de datos o servicios externos pueden convertirse en un cuello de botella. El caché ayuda a:
- Reducir las demoras en consultas repetitivas.
- Disminuir la carga en bases de datos y APIs de terceros.
- Aumentar el rendimiento del servidor.
- Suavizar los picos de carga.
2. Redis — caché distribuido
Redis es la solución más popular para caché externo en Go. Funciona como un almacenamiento en memoria con soporte para TTL, persistencia y estructuras de datos complejas.
Conexión a Redis en Go
Para trabajar con Redis en Go, se usa principalmente la biblioteca go-redis. Instalación:
go get github.com/redis/go-redis/v9Ejemplo: caché simple con TTL
package main
import ( "context" "fmt" "time" "github.com/redis/go-redis/v9")
var ctx = context.Background()
func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", })
// Establecemos un valor con TTL de 10 segundos err := rdb.Set(ctx, "user:42", `{"name":"Alice"}`, 10*time.Second).Err() if err != nil { panic(err) }
// Obtenemos el valor val, err := rdb.Get(ctx, "user:42").Result() if err != nil { fmt.Println("Fallo de caché") } else { fmt.Println("Acierto de caché:", val) }}Estrategias de invalidación de caché en Redis
- TTL (Time To Live) — eliminación automática al expirar el tiempo.
- Invalidación perezosa — limpieza del caché al actualizar datos.
- Eliminación por clave — llamar a
DELpara una clave específica. - Patrón "Cache-Aside" — la aplicación gestiona el caché: primero verifica Redis, en caso de fallo carga desde la base de datos y lo guarda en caché.
3. Caché en memoria en Go
Para caché local sin dependencias externas, se pueden usar estructuras nativas de Go o bibliotecas especializadas.
Caché en memoria simple con sync.Map
package main
import ( "fmt" "sync" "time")
type CacheItem struct { Value interface{} Expiration int64}
type Cache struct { items sync.Map}
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) { c.items.Store(key, CacheItem{ Value: value, Expiration: time.Now().Add(ttl).UnixNano(), })}
func (c *Cache) Get(key string) (interface{}, bool) { item, found := c.items.Load(key) if !found { return nil, false } cacheItem := item.(CacheItem) if time.Now().UnixNano() > cacheItem.Expiration { c.items.Delete(key) return nil, false } return cacheItem.Value, true}
func main() { cache := &Cache{} cache.Set("key1", "value1", 5*time.Second) val, ok := cache.Get("key1") if ok { fmt.Println("Encontrado:", val) }}Biblioteca freecache
freecache es una biblioteca de caché en memoria de alto rendimiento con soporte para TTL y bajo consumo de memoria. Instalación:
go get github.com/coocood/freecachecache := freecache.NewCache(100 * 1024 * 1024) // 100 MBcache.Set([]by