1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Los cachés se introducen para reducir la carga de la base de datos, pero herramientas fáciles de usar como Redis tienden con el tiempo a volverse dependencias similares a un almacenamiento persistente
  • El problema no es la función de persistencia de Redis, sino el flujo operativo en el que un componente introducido al principio como caché volátil termina entrelazándose con el estado central de la aplicación
  • memcached, desde su propia definición oficial, es un sistema distribuido de caché de objetos en memoria y no está pensado para almacenamiento en disco, por lo que es más fácil tratarlo como una carga de trabajo de caché sin estado
  • Varias instancias de memcached no se reparten por el servidor sino por el cliente, usando una lista de URL y el hash de las claves; los nodos con fallas salen del hasher y más adelante intentan reconectarse
  • En vez de agregar primero un caché porque “la base de datos está lenta”, primero hay que revisar las consultas lentas y los índices faltantes

El momento en que Redis pasa de caché a almacenamiento

  • Cuando operas infraestructura, la petición de “necesitamos un caché” aparece con frecuencia, y es fácil pensar primero en Redis por ser familiar y tener muchas funciones
  • La página principal de Redis destaca Redis Iris como un motor de contexto en tiempo real para apps de IA, pero es una dirección entendible considerando que Redis es una empresa que necesita generar ingresos
  • Despliegas Redis, entregas la cadena de conexión, y al principio funciona como un caché confiable

Los problemas que aparecen unos meses después

  • Con el tiempo, como cache.set("key", "value") es mucho más simple que INSERT INTO table VALUES ('key', 'value'), la gente empieza a tratar Redis así:
    • Como un componente que siempre existe, un lugar para conservar datos. Una base de datos de facto
    • Empiezan a entender REmote DIctionary Server no como un caché volátil, sino como un almacenamiento permanente
  • Ni tú ni tus colegas del equipo de operaciones suelen darse cuenta de esto, y como creen que el caché será tratado como volátil, el sistema de alertas tampoco lo detecta
    • El problema recién sale a la luz cuando haces algo en Redis, como una actualización, mover un nodo o que se salga la bandeja HDD de un servidor RAID0
  • El problema central no es que Redis no tenga persistencia, sino el desajuste en la suposición de que la gente no trata como caché al Redis que fue introducido como caché
  • Cuando descubres la dependencia demasiado tarde, Redis ya está demasiado entrelazado con la aplicación y es difícil retirarlo; al final terminas dándole mantenimiento y monitoreándolo como si fuera una “mascota”

Por qué memcached es más directo para el rol de caché

  • memcached es un “sistema gratuito, open source, de alto rendimiento y caché distribuido de objetos en memoria”, un caché de propósito general pensado para reducir la carga de la base de datos y acelerar aplicaciones web dinámicas
  • En frameworks que soportan caché enchufable como Django, puedes cambiar el backend de caché
  • Aunque tenga menos funciones que Redis, hay razones para elegir memcached porque sus características operativas son más simples
    • Es fácil manejar el downtime: muchas bibliotecas cliente ignoran las excepciones de conexión, y un get simple puede devolver un valor por defecto o None incluso si el servidor está caído
    • memcached no trae clustering integrado, y por eso mismo el clustering resulta más conveniente
      • Si configuras varias URL en la biblioteca cliente, elige la instancia de destino según el hash de la clave
      • Si la llamada del cliente detecta que una instancia cayó, elimina el nodo del hasher e intenta reconectarse automáticamente después de cierto tiempo
    • La carga de la persistencia se reduce estructuralmente: como memcached no guarda en disco, es más fácil programarlo donde quieras como una carga de trabajo sin estado
  • Con Redis también puedes construir una operación parecida, pero la arquitectura de memcached está más alineada con este enfoque, así que resulta más intuitivo tratarlo como caché
  • memcached es una aplicación relativamente simple, y una razón para elegirlo es que incluso si ejecutas decenas de instancias con un caché de unos 64MB, el overhead es casi nulo
  • Muchos problemas de “la base de datos está lenta” en realidad empiezan en consultas lentas o índices faltantes, así que junto con agregar caché también hay que revisar la optimización de consultas
  • Si te interesan las decisiones de diseño de memcached, en el blog de memcached hay muchos textos interesantes; uno de ellos es “¿Cuánto tarda realmente esa respuesta? (How Long Does That Response Take… For Real?)”, publicado en mayo

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Hacker News
  • Redis es una gran tecnología, pero creo que sufre por intentar hacer bien al mismo tiempo dos roles distintos: estructuras de datos persistentes y caché volátil
    En Redis real tampoco se mezclan tan bien, así que la persistencia se activa o desactiva de forma global
    Para caché pura usaría memcached o algo equivalente, y solo usaría Redis con persistencia activada cuando necesite estructuras de datos como un marcador
    En $WORK no adoptamos ninguno de los dos, y para la capa de caché de trabajos lentos dejamos los datos tanto en el sistema de archivos como en una tabla de la BD usada como almacén clave-valor
    La BD ayuda a coordinar el thundering herd, las lecturas desde el mismo servidor solo pegan al sistema de archivos, y las lecturas desde otros servidores revisan la BD una vez y luego lo conservan en el sistema de archivos
    Se podría reemplazar la capa del sistema de archivos con memcached, pero hasta ahora ha funcionado muy bien

    • Después de haber trabajado con Memcachedb (memcache + bdb para persistencia) a fines de los 2000, llegué casi a la misma conclusión
      Redis claramente tenía muchas más funciones, y antirez era una persona encantadora y sorprendentemente humilde, así que entiendo por qué Redis se volvió más popular
      Aun así, para mí memcached siempre fue la cumbre de elige tecnología aburrida
      Como ingeniero de plataforma, puedo dar soporte a ambos, pero cuando los desarrolladores empiezan a usar funciones avanzadas de Redis como persistencia, replicación y clustering, intento confirmar que realmente entienden las desventajas de esa decisión
    • Solo usando una tabla de BD como almacén clave-valor y acompañándola con el sistema de archivos, ya se pueden hacer muchísimas cosas antes de pagar el costo de configurar un almacén de caché dedicado
      Cada vez que propongo esta solución, he terminado peleando incontables veces en entornos de ingeniería con gente inexperta que siente que la caché tiene que estar sí o sí en un almacén dedicado
  • Que sea memcache no significa para nada que puedas evitar estos problemas
    A mediados de los 2000 trabajé con un sistema escalado que usaba memcache, y los desarrolladores cayeron exactamente en las mismas trampas que el artículo pone como ejemplo para Redis
    Intentaron esquivar las leyes de los sistemas distribuidos con memcache, y por adicción a la caché dimensionaron el clúster de servidores asumiendo que memcache estaría activo; cuando fallaba, de repente explotaba como si fuera un DDoS
    También había amplificación de escritura cuando algún host expulsaba una clave con TPS alto y todos los demás hosts golpeaban al servicio dependiente para volver a poblarla; las hot keys creaban hot hosts, y levantar memcached junto al daemon del servicio llevaba a picos misteriosos de CPU
    Las llamadas a memcache también podían perderse en un agujero negro por la pegajosidad de entradas DNS antiguas
    Todo eso se podría haber evitado usando mejor memcache, pero la tentación de abusarlo era demasiado fuerte

  • Creo que casi todos los problemas de Redis/Valkey que menciona el autor ya los vi en producción
    Hubo una caída porque Valkey no tenía política de memoria, se comió toda la memoria y produjo errores al escribir el append-only file, y también vi casos donde la escritura del AOF falló porque el disco literalmente se llenó
    También hubo errores 500 porque se asumía por completo que Redis estaría vivo, ejecutándose y lleno con todos los datos de usuario, sin ningún mecanismo para volver a una ruta lenta
    También vi usos creativos de sorted sets y otras estructuras de datos dependiendo de que ese conjunto jamás fuera expulsado
    Aun con todo lo observado en campo, sigue siendo difícil recomendar memcache antes que Redis
    Diseñar la app para que tenga una disposición de caché amigable con memcache puede ser complicado, y si un equipo lo bastante grande usa memcache, es muy probable que termine encontrando el camino hacia necesitar Redis
    Entonces terminas manteniendo 2 tecnologías de caché

    • Si alguien decide usar Redis para algo que no sea caché, en la práctica ya tienes 2 tecnologías de caché
      Una instancia de Redis configurada para caché no se puede usar para otros fines, la instancia de caché debe tener expulsión y la instancia no orientada a caché no debe tenerla
      Al final necesitas un segundo Redis con configuración distinta
      Sinceramente, diseñar la app con una disposición de caché amigable con memcache es lo mismo que diseñarla con una disposición de caché amigable con Redis
      El patrón de estas cachés de aplicación es el mismo: obtén, y si no está, calcula y guarda
    • Si aún no existe, yo haría una interfaz de abstracción donde pidas la clave y pases una función asíncrona o lambda para traer el valor desde el origen en caso de cache miss
      var value = cache.lookup( keyname, () => db.query(...), TimeSpan.FromMinutes(5) // or CacheOptions );
      Así puedes ir de inmediato a la ruta alternativa o insertar cuando haya un cache miss
    • No mantener 2 tecnologías de caché siempre es un argumento ganador
  • Otra característica poco mencionada de memcache es que todas sus operaciones son O(1) por diseño
    Fue una decisión deliberada de diseño, así que tiene limitaciones, pero garantiza que no haya pausas aleatorias en operaciones simples
    En cambio, Redis, por su diseño de núcleo monohilo, puede ejecutar operaciones de complejidad arbitraria, y desde la perspectiva del desarrollador eso puede hacerte sentir muy listo al usarlas, pero todo lo demás tiene que esperar hasta que esa operación termine

  • En proyectos open source o programas con mantenimiento a largo plazo, esto pasa seguido
    cuando la base de código crece, al final se empiezan a soportar cosas que no estaban en el plan original
    cuando aumentan las funciones, también aumentan los usuarios; algunos usan solo las funciones viejas y otros adoptan las nuevas, y al final cierto valor se vuelve el valor predeterminado de facto y deja de parecer una opción
    si tomamos Redis como ejemplo, al desactivar AOF funciona como un caché volátil en memoria, pero la mayoría ni siquiera lo piensa así
    por eso aparece la lógica de que es mejor tener menos funciones y más simplicidad, y en este contexto Memcached es un ejemplo de ese enfoque de camisa de fuerza
    para equipos grandes tiene todo el sentido, pero los proyectos open source necesitan actualizaciones periódicas para seguir obteniendo financiamiento o contribuciones, así que hay una tensión inherente
    a veces eso lleva a forks o proyectos derivados especializados en un nicho
    personalmente, creo que no hay una respuesta correcta y que depende del contexto
    porque la comunicación tampoco es gratis

    • Que la comunicación no sea gratis es justo el problema que tengo con los microservicios
      parece que los desarrolladores no lo supieran en absoluto
    • El ejemplo más claro es que la gente cree que Redis solo funciona como un caché que pierde datos cuando hay un fallo o un apagado
      creo que eso viene de haber reemplazado Memcached por Redis esperando exactamente lo mismo
    • A gran escala, AOF termina provocando incidentes, así que se apaga
      aun así sigue siendo un gran caché
  • En los últimos años he trabajado bastante con Flask, no de tiempo completo, pero sí como parte del stack tecnológico de un pequeño negocio de comercio electrónico
    con MongoEngine, SQLAlchemy, Celery y el stack de Python para Google/eBay/Shopify me he topado con toda clase de trampas y rarezas, pero con Redis nunca me ha pasado eso
    quizá sea porque no le damos privilegios de administrador a cualquiera que crea que Redis es un almacén persistente, pero sinceramente diría que Redis es una tecnología muy sólida y bien diseñada
    la API es extremadamente simple, y cada vez que tienes que hacer algo un poco raro, hay una forma razonable y bien pensada de hacerlo

    • Justo ahora estoy empezando un proyecto con Flask, SQLAlchemy y Celery, así que me gustaría escuchar más sobre por qué habría que evitar Celery y qué usar en su lugar
    • En mi mundo, sistemas de caché como memcached y Redis son solo cachés de meter y sacar
      supongo que se podría usar un sistema de invalidación como el etiquetado
      de verdad me da curiosidad qué cosas raras se pueden hacer con un sistema de caché, o qué hace la gente con un caché además de simplemente almacenar datos en caché
  • Me gusta memcached, pero si configuras Redis como caché volátil y luego la gente lo trata como un almacén de datos persistente, eso no es culpa de Redis
    la comparación es especialmente rara porque memcached tampoco es persistente

    • En muchas empresas, probablemente en la mayoría, Redis no se ve ni se opera como un caché que puede desaparecer en cualquier momento, sino como una base de datos de producción duradera
      si nadie te dice lo contrario, tampoco es tan raro que un desarrollador nuevo lo dé por hecho
  • Memcached fue el salvador del caching cuando salió
    también me gusta que Brad Fitzpatrick lo haya creado para LiveJournal en 2003
    cada publicación en el feed de usuarios podía tener restricciones de acceso distintas, y gracias a eso se podían cachear publicaciones o páginas completas
    lo usé durante varios años junto con Ruby on Rails, las páginas se volvieron más rápidas y simplemente funcionaba
    la desventaja, y a la vez ventaja en velocidad, era que el caché se guardaba en memoria y no en disco
    si el volumen de datos a cachear es grande y el sitio es grande, el costo de hosting puede dispararse
    en esos casos, Solid Cache fue mi salvación
    en el proyecto en el que trabajo ahora, el caché supera los 100 GB, se guarda en el disco de PostgreSQL, se consulta rápido con índices y Rails maneja automáticamente la expiración para eliminar esas filas
    si el tamaño del caché fuera menor y ya estuviera usando Redis, probablemente simplemente usaría Redis
    pero si la velocidad fuera la prioridad número uno, haría benchmarks entre Memcached y Redis

  • Que memcached sea temporal y que la gente lo use o no como si fuera persistente son asuntos distintos
    si la tasa de aciertos del caché parece de 99.9% y siempre está ahí, tarde o temprano alguien va a escribir código que dependa de ese comportamiento
    me pregunto si en modo de desarrollo la biblioteca cliente podría ayudar devolviendo null como en un 10% de los casos

  • memcached es ridículamente más rápido que Redis para el trabajo simple de caché clave-valor
    tiene hilos y está altamente optimizado para hacer una sola cosa muy bien
    en cambio, Redis se siente más como una especie de heap compartido de Python para usos arbitrarios, con todas sus estructuras de datos y su hilo único, etc.
    en Notion usan Redis para varias cosas, pero el caching real se lo dejan a memcached

    • Se puede comprobar que para clave-valor no es tan así
      el promedio es algo como 300 microsegundos frente a 350 microsegundos por lectura
      y que sea de un solo hilo tampoco importa tanto, porque no es un cuello de botella de CPU sino de E/S reactiva
    • Los hilos no son gratis
      te permiten usar más núcleos de CPU, pero si la carga no es tan alta, memcached de un solo hilo usa menos CPU que uno multihilo