Introducción: por qué el caché es crítico para el rendimiento de C++
En el desarrollo moderno de alta carga con C++, el caché ha dejado de ser una opción para convertirse en un elemento obligatorio de la arquitectura. Sin él, cualquier sistema CRUD o microservicio choca rápidamente con los límites de la base de datos, y la latencia de las solicitudes crece exponencialmente. A nivel avanzado, nos enfrentamos al dilema: usar sistemas externos (Redis, Memcached) o implementar nuestra propia solución en memoria en C++. En este artículo, analizaremos los pros, los contras y las implementaciones concretas de cada enfoque, basándonos en la práctica con colas de red, bloqueos y serialización.
1. Redis: flexibilidad y estructuras de datos
Redis no es solo un almacén clave-valor. Su principal ventaja es un rico conjunto de tipos de datos: cadenas, hashes, listas, conjuntos, conjuntos ordenados. Para C++, existen varios clientes: redis-plus-plus (recomendado) y hiredis (de bajo nivel).
Ejemplo de conexión y operaciones básicas con redis-plus-plus
#include <sw/redis++/redis++.h>#include <iostream>
int main() { // Conexión a Redis (por defecto localhost:6379) sw::redis::Redis redis("tcp://127.0.0.1:6379");
// Establecer valor con TTL de 60 segundos redis.set("user:1001", "John Doe", std::chrono::seconds(60));
// Lectura auto val = redis.get("user:1001"); if (val) { std::cout << "Usuario: " << *val << std::endl; }
// Trabajo con hashes redis.hset("session:abc", "token", "xyz123"); redis.hset("session:abc", "expires", "3600"); auto token = redis.hget("session:abc", "token"); std::cout << "Token: " << *token << std::endl;
return 0;}Características avanzadas: uso de transacciones mediante redis.multi() y redis.exec(), así como Pub/Sub para notificaciones de invalidación de caché. Redis es ideal cuando se necesitan almacenar estructuras complejas con operaciones atómicas (por ejemplo, incremento de contador).
2. Memcached: velocidad y simplicidad
Memcached es un caché distribuido ligero, optimizado para almacenar datos planos (cadenas y blobs binarios). No soporta transacciones ni estructuras complejas, pero gana en velocidad y consumo mínimo de memoria. Para C++, usamos la biblioteca libmemcached.
Ejemplo de trabajo con Memcached a través de libmemcached
#include <libmemcached/memcached.h>#include <iostream>#include <cstring>
int main() { memcached_st *memc = memcached_create(nullptr); memcached_server_st *servers = nullptr; memcached_return_t rc;
// Agregar servidor servers = memcached_server_list_append(servers, "127.0.0.1", 11211, &rc); rc = memcached_server_push(memc, servers); if (rc != MEMCACHED_SUCCESS) { std::cerr << "Error: " << memcached_strerror(memc, rc) << std::endl; return 1; }
// Escritura con TTL de 30 segundos const char *key = "config:db_host"; const char *value = "10.0.0.1"; rc = memcached_set(memc, key, strlen(key), value, strlen(value), std::time(0) + 30, (uint32_t)0); if (rc != MEMCACHED_SUCCESS) { std::cerr << "Set falló: " << memcached_strerror(memc, rc) << std::endl; }
// Lectura size_t val_len; uint32_t flags; char *retrieved = memcached_get(memc, key, strlen(key), &val_len, &flags, &rc); if (rc == MEMCACHED_SUCCESS) { std::cout << "Host BD: " << retrieved << std::endl; free(retrieved); }
memcached_free(memc); return 0;}¿Cuándo elegir Memcached? Si necesita un caché simple para objetos serializados, sin necesidad de almacenar estructuras complejas. Memcached es ideal para almacenar en caché resultados de consultas SQL o fragmentos HTML.