Desmitificando Memcached (I)

Me he decidido a hacer este post debido a la cierta oscuridad que hay sobre memcached en muchos entornos de desarrollo web. En webs de alto tráfico está instalado (y de hecho gracias a ello las webs aguantan) y muchas veces los desarrolladores lo usan sin entender realmente qué está sucediendo.

Y en el mundillo freelances y pymes, donde se suele tener servidores compartidos no es que esté precisamente muy extendido el uso de este u otros sistemas de cacheado. Vamos pues a intentar aportar algo de luz al respecto.

En el mundillo PHP, veremos que se usa indistintamente las palabras memcache y memcached sin tener muy claro cuál es la correcta. Para aclararlo, en resumen:

– Memcached: Es un sistema distribuido de propósito general para caché que se instala en un servidor y funciona como un servicio más de la máquina, escuchando por un puerto que configuramos. (Ver Wikipedia). Este servidor es completamente independiente de PHP y se puede usar en general desde cualquier lenguaje de programación que tenga librerías para interactuar con él.

– Librería memcache de PHP (Ver PHP.net): Es una librería ya algo antigua que nos encapsula el acceso a Memcached. Sirve como primera aproximación y es muy fácil de instalar y activar pero no aprovecha todo el potencial de memcached.

– Librería memcached de PHP (Ver PHP.net): Es una librería bastante más moderna, algo complicada de instalar en según qué distribuciones Linux (pues requiere compilar las librerías libmemcached y la propia librería si queremos habilitar serializadores especiales como IgBinary) pero que nos da mejor rendimiento y aprovecha mejor las posibilidades de memcached.

Hechas estas aclaraciones, vamos a explicar qué es y sobretodo qué no es memcached y sus problemas (que también los tiene).

¿Qué es memcached? ¿Para qué nos puede servir?

Como hemos dicho, memcached es un sistema de cacheado de propósito general. Cachear es algo que en aplicaciones con poca actividad no nos supondrá ningún beneficio pero que en entornos de alta disponibilidad es clave para asegurar que una subida de actividad no nos dejará sin servicio. El concepto es simple, si algo cambia poco, guárdatelo ya procesado y listo para servir sin tener que acceder a la BBDD y procesar esos datos. Aunque una conexión a BBDD y su query vaya rápido, es mucho más rápido leer directamente de disco (algunos sistemas básicos de caché hacen eso, precalcular cosas y dejarlas en fichero ini o xml) y aún más rápido guardarlo en un buffer de memoria (como hacen Memcached o también por ejemplo APC, del cual hablaré en próximos posts y cuya mejora respecto a memcached es que los datos no se guardan serializados).

En webs como Privalia o otras de volúmenes similares tenemos datos que cambian poco y se piden mucho, como por ejemplo los detalles de los productos, los detalles de las campañas, etc… Parece lógico almacenar estos datos en un sitio intermedio durante un periodo razonable de tiempo para acelerar drásticamente el acceso a los mismos sin necesidad de entrar a MySQL.

También es bastante habitual guardar los datos de sesión en memcached en vez de en disco (que es la configuración por defecto de PHP). Con ello, podemos tener una configuración de servidores con múltiples frontales Apache balanceados sirviendo el mismo código y todos tirando de memcached para las sesiones sin tener que configurar una carpeta de disco compartida por todos. De hecho, es posible configurar PHP para que directamente use memcached de forma transparente para las sesiones y nos olvidamos de este tipo de problemas.

¿Cómo funciona?

Todas las librerías de memcached tienen los siguientes métodos básicos

Connect: Donde básicamente pasamos ip, puerto y alguna cosilla más
SetData: Donde pasamos los datos a guardar (podemos pasar directamente arrays y objetos que la propia librería se encarga de serializar), una clave alfanumérica para identificarlo y el tiempo de cacheado.
GetData: Donde pasamos la clave a recuperar, y nos devuelve los datos unserializados.
FlushData: Donde pasamos la clave a eliminar (esto puede servir para que un cambio en backoffice elimine datos cacheados ya incorrectos)
FlushAll: Elimina todas las claves de memcached.

Y… poquito más. Bien es cierto que en las nuevas versiones podemos configurar N servidores, definir el porcentaje de peticiones que irán a uno u otro, configurar el serializador, etc… pero la base es esta.

La estrategia es sencilla y tiene 3 pasos:
– Justo antes de hacer una query a BBDD cuyo resultado cambia poco, generamos una key y miramos si el dato ya esta cacheado.
– Si lo está, no hacemos la query y devolvemos los datos cacheados.
– Si no lo está, hacemos la query, cacheamos los datos y los devolvemos.

Problemas de memcached

Sí, no todo son ventajas y hay que tenerlas en cuenta. Cosas con las que me he encontrado serían las siguientes:

– Por cada clave solamente se permite almacenar 1 Mb de datos serializados. Esto es una auténtica burrada y cabe medio quijote pero según lo que queramos almacenar podemos pasarnos. Lo cachondo del caso es que no salta excepción sino que simplemente devuelve false.

– El servidor de memcached tiene definida una memoria de uso. Si cacheamos muchas cosas, se puede producir un overflow y las claves más antiguas empiezan a expirar para hacer sitio a las nuevas. Mucho cuidado pues con configurar correctamente esto.

– En alta escalabilidad, hasta que está todo cacheado puede darse el caso de muchas peticiones intentando cachear lo mismo simultáneamente. Este fenómeno es conocido como cache storming y aunque dura poco podemos tener problemas en aplicaciones donde de golpe la demanda aumenta drásticamente.

– No se pueden hacer querys para recuperar las keys almacenadas ni un rango de las mismas. Es decir, mucho cuidado con elegir las keys en los setData ya que nos podemos encontrar con no saber regenerarlas y por tanto no poder expirar keys concretas.

– Si no dimensionamos bien la aplicación, podemos estar haciendo muchos setData y pocos getData (porque el tiempo de expiración es muy bajo o porque cacheamos datos de poca consulta).

– Si confiamos en que memcached aguantará la carga y no optimizamos la aplicación, el día que se cae memcached, adios aplicación web en escasos minutos.

– Si los datos a cachear son muy grandes, la serialización de los mismos se vuelve muy costosa. Esto es especialmente grave con la vieja librería memcache y subsanado en parte con memcached+igbinary. Siempre irá más rápido que una query, especialmente si hacemos varias joins, pero tal vez la ganancia no sea tan grande como esperamos.

– Memcached no lleva autenticación. Es decir, cualquiera con acceso al servidor, si conoce las keys puede recuperar nuestros datos. Debido a esto, raramente está instalado en servidores compartidos.

¡Todos los que podáis, a usar memcached!

Teniendo en cuenta todo lo que he comentado, si decidimos tirar de memcached veremos que nuestra aplicación web aumenta mucho en tiempo de respuesta y capacidad de servicio sin hacer mucha cosa más que modificar algunas funciones del modelo para intentar sacar los datos de memcached antes de ir a la base de datos.

Es muy sencillo de instalar el servidor y la librería memcache básica y con una pequeña prueba de stress con Jmeter o similar veremos cómo mejora la cosa.

Esto es todo por hoy, en próximos posts ejemplos de código, ventajas e instalación de Memcached y Igbinary y APC.

You may also like...