Almacenamiento en caché en Go: Redis, Memcached y soluciones integradas
El almacenamiento en caché es una de las herramientas clave para mejorar el rendimiento de aplicaciones y servicios web. En el ecosistema de Go (Golang), el desarrollador tiene acceso tanto a sistemas de caché externos (Redis, Memcached) como a soluciones integradas como sync.Map o las bibliotecas bigcache y freecache. En este artículo, analizaremos los pros y los contras de cada enfoque, proporcionaremos ejemplos de código y ayudaremos a elegir la herramienta óptima para su proyecto.
1. Caché integrado: sync.Map y map con mutex
Go ofrece primitivas básicas para crear un caché directamente en la memoria de la aplicación. La forma más sencilla es usar un map normal con un mutex para la seguridad en concurrencia.
Ejemplo: caché simple seguro para concurrencia
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}Para escenarios más complejos, Go proporciona sync.Map — un mapa concurrente optimizado que es más adecuado para casos donde las claves se escriben una vez y se leen muchas veces.
¿Cuándo usar el caché integrado?
- Micro-caché (durante una sola solicitud o sesión).
- Aplicaciones singleton sin arquitectura distribuida.
- Simplicidad — no es necesario levantar servicios externos.
Desventajas: falta de invalidación automática, tamaño limitado (depende de la memoria de la aplicación), sin persistencia.
2. BigCache y FreeCache: soluciones ligeras en memoria
Las bibliotecas bigcache y freecache resuelven el problema de la memoria limitada y el GC (recolección de basura). Utilizan búferes preasignados y minimizan la carga en el recolector de basura de Go.
Ejemplo con 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}Ejemplo con 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 seg val, _ := cache.Get([]byte("key")) fmt.Println(string(val)) // value}Ambas bibliotecas admiten TTL (tiempo de vida) y eliminación automática de entradas antiguas. BigCache es mejor para grandes volúmenes de datos (cientos de gigabytes), FreeCache es para escenarios con operaciones frecuentes de escritura/lectura.
3. Redis: caché distribuido para producción
Redis es la opción más popular para el almacenamiento en caché en proyectos Go. Funciona como un servidor independiente, admite estructuras de datos complejas, persistencia y agrupación en clústeres.
Conexión a Redis en 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: 0, })
err := rdb.Set(ctx, "key", "value", 0).Err() if err != nil { panic(err) }
val, err := rdb.Get(ctx, "key").Result() if err != nil { panic(err) } fmt.Println("key", val)}```