Jul 02

Apache Cassandra vs MongoDB

 Llevamos haciendo pruebas unos días para comparar Apache Cassandra y MongoDB. Nuestro objetivo inicial es insertar datos más o menos grandes (unos 80KBytes)  a razón de 5 inserciones por segundo. No es nada del otro mundo, a poco que nos pongamos posiblemente hasta una base de datos SQL normal nos valga. Sin embargo, queríamos jugar con estas dos bases de datos noSQL y ver qué pasa.

En un mismo servidor hemos montado ambas bases de datos y realizado las inserciones A veces simultáneamente en las dos bases de datos, a veces sólo en una.

En Cassandra configuramos el tiempo de vida de los datos en unas 5 horas, de forma que los datos más viejos de 5 horas se borrarán automáticamente. Mongo tiene dos opciones, una, al igual que Cassandra es la de tiempo de vida, pero otra que nos parece más interesante es la de espacio en disco duro. Así que configuramos Mongo para que borrara automáticamente datos viejos cuando la colección ocupara 10 Gigas.

Pues bien, en estas condiciones, tanto antes de empezar borrados automáticos como después, ambas bases de datos van sobradas. Pueden perfectamente insertar datos y permitir las consultas simultáneamente. Sin embargo, echando un ojo al consumo de recursos, vemos que mongo consume bastante menos en CPU y aunque cassandra tampoco consume demasiado, sí tiene unos picos de consumo de CPU bastante importantes periódicamente. Seguramente algunas de las tareas de mantenimiento del propio cassandra que saltan de vez en cuando.

Logo mongodbCuando empieza el borrado, vemos en disco que mongo tiene sus 10 Gigas y no se pasa ni un pelo. Vemos sin embargo que cassandra sigue creciendo y que hay que esperar a alguna de esas tareas de mantenimiento para que el disco realmente reduzca su espacio.

En cuanto a consultas, mucho mejor mongo. En cassandra nos pasan dos cosas. Un simple select count(*) da timeout, sin necesidad de que haya más allá de un par de miles de registros. La consulta en cassandra de los datos, poniendo bien los índices en la condición y tal va bien, pero nos da esos misteriosos problemas que da a veces cassandra diciendo que no hay bastantes réplicas (¿1 necesaria, 0 disponibles? ¿con un solo servidor que está arrancado?) y sucede cuando consultas en el periodo de tiempo que acaba de borrar por pasarse el tiempo de vida, porque si consultamos dentro de la zona termporal en la que todavía hay datos, entonces los devuelve sin errores.

Bueno, seguro que se puede arreglar y optimizar, pero sin haberse matado en configuraciones, mongo va bien y cassandra hace alguna cosa rara.

Vamos más allá con la prueba. Se supone que cassandra es más rápido en inserciones, así que decidimos insertar datos de pocos bytes (unos 40 bytes), pero del orden de 250 por segundo. Vemos que ambas bases de datos aguantan el ritmo. Vamos subiendo a 500 por segundo, 750 por segundo, etc, etc…. Sorpresa. Mongo va mejor que Cassandra. Mongo aguanta cerca de 1000 por segundo (en nuestro servidor, con nuestro disco, si lo haces en otro será otro resultado), mientras que cassandra ya no puede con 750.

Insisto, no nos hemos matado en optimizar, inserts a pelo, nada de batch, ni bulk inserts ni similar. Lo tenemos pendiente de probar.

De todas formas, y antes estas primeras impresiones, creo que nos quedamos con mongo, al menos para nuestras necesidades. Nos quedan pruebas en cluster,, donde siempre según los de Cassandra, Cassandra es mejor en el sentido que de que si pones el doble de servidores, tienes el doble de todo, mientras que con mongo no es así  En cualquier caso, haremos las pruebas por hacerlas, puesto que en producción no tendremos clusters.

Jun 29

Primeras impresiones de Apache Cassandra

Apache CassandraEstán de moda las bases de datos BigData, bases de datos que son capaces de manejar cantidades ingentes de datos con multitud de clientes simulttáneos insertando, modificando y consultando datos. Al quedarse pequeñas las bases de datos tradicionales, las grandes compañías como google, facebook, … desarrollaron sus propios sistemas de base de datos : Google desarrolló BigTable y Facebook comenzó lo que ahora es Cassandra.

Así que me he puesto a jugar con Apache Cassandra y he aquí mis primeras impresiones.

Lo primero es que me ha parecido muy simple de instalar y poner en marcha. Un simple zip para desempaquetar y arrancar, o bien un instalador adecuado para el sistema operativo. Por ejemplo, el de Windows lo instala directamente como servicio si así lo deseamos.

Conectar varios servidores de Apache Cassandra entre sí para que formen un nodo multi-cluster también es relativamente sencillo. No he visto una documentación que diga correctamente todo lo que hay que tocar, pero no son más que cuatro o cinco propiedades en el fichero de configuración y no cuesta mucho encontrarlas tras revisar un poco la documentación para tenerlo funcionando.

Finalmente, desde java con el conector adecuado o bien desde la misma línea de comandos que ofrece Apache Cassandra, se pueden crear los primeros keyspaces, column families y datos. No he tenido demasiados problemas para hacer funcionar correctamente los ejemplos que encontré por ahí o mis propias pruebas.

Hablando de pruebas, me dio por comparar velocidad entre Apache Cassandra y PostgreSQL. No hice una prueba exahustiva, simplemente cree en ambas bases de datos una tabla igual, con las mismas columnas e índices, un solo servidor Cassandra, sin cluster ni nada, y partiendo de cero registros en cada tabla, lance dós hilos java, uno a hacer "update or insert" en una base de datos y otro lo mismo en la otra. Para Cassandra sólo insert, ya que si la fila existe, hace el update automáticamente. El resultado fue que PostgreSQL es más rápido inicialmente (unos 150 ms por cada 1000 insert/update frente a los 400 ms de Cassandra), pero tras estar funcionando el programa un rato largo y tener cerca de 2 millones de registros en la tabla, PostgreSQL tardaba unos 1700 ms por cada 1000 insert/update, mientras que Cassandra se mantenía firmemente en 400 ms.

Me ha parecido impresionante la facilidad de uso y su posibilidad de distribuir datos entre varios cluster, haciendo réplicas en varios discos o bien repartiendo los datos. Sin embargo, le he visto una gran pega a Apache Cassandra, precisamente su punto fuerte se convierte en su punto débil.

Cassandra está pensada para montar N servidores, cada uno con su ejecutable de Cassandra, trabajando en paralelo sobre M discos duros. Es capaz de manejar mogollón de datos eficientemente porque tiene a su disposición varios ordenadores entre los que repartir la carga y varios discos duros en los que repartir los datos. No es lo mismo que miles de clientes accedan simultáneamente a miles de datos dentro del mismo servidor y mismo disco, que Cassandra sea capaz de distribuir esos clientes en función de la carga de cada servidor y de lo ocupados de los discos. De hecho, los clientes puede conectarse a cualquiera de los ejecutables de Cassandra indistintamente. Pero justo esa distribución de datos en varios discos tiene una pega.

Si se hace una consulta de datos, Cassandra tendría que recorrer todos los discos para asegurarse que encuentra todos los datos solicitados. La busqueda de datos, pensada así, no es eficiente, así que Cassandra de alguna forma prohíbe hacer búsquedas que no estén planificadas previamente. Hablando lenguaje de programador, en la cláusula WHERE de una consulta no podemos poner lo que nos dé la gana. Hay que indicarle a Cassandra, en el diseño de las tablas, cómo vamos a consultar, de forma que Cassandra sea capaz de repartir los datos adecuadamente en los discos y sea luego capaz de saber en qué discos están los datos que estamos pidiendo.

Cassandra repartirá los datos en los discos de acuerdo a unos criterios, de forma que luego sea capaz de encontrarlos eficientemente. El primer criterio es la clave primaria de la tabla, en función de su valor, va a un disco u otro. Siempre se puede consutar por clave primeria, porque Cassandra sabe en qué disco estará ese dato.

Si queremos buscar por otros campos que no sean clave primaria, Cassandra nos obliga a crear índices secundarios para esos campos. Usando estos índices, Cassandra también sabrá en qué disco está cada dato, por lo que las búsquedas serán más eficientes. Pero la misma documentación de Cassandra nos advierte, no es adecuado crear un índice secundario de un campo que tenga valores distintos siempre. Por ejemplo, si nuestra tabla es de una flota de camiones, podemos crear un índice por marca de camión, ya que habrá varios camiones de la misma marca. Pero si estamos guardando las posiciones del camión obtenidas de sus gps, no podemos crear índices por la fecha/hora de la posición, ya que es muy probable que no haya dos fechas/horas iguales en todas las tablas, ni tampoco por latitud/longitud, por el mismo motivo.

¿Qué dice Cassandra que hagamos entonces para ese tipo de campo si queremos buscar por él?. Pues aprovechar que las columnas de las tablas son dinámicas. Cada fila dentro de una misma tabla puede tener columnas diferentes a otras filas y las columnas se ordenan por orden alfabético de nombre de columna. Por ejemplo, si se quieren guardar las posiciones históricas de un camión obtenidas de su GPS, se crearía una tabla en el que la clave sería el identificador de cada camión, es decir, habría una fila por cada camión. En cada fila, cada vez que se obtiene una nueva posición del camión que corresponde a esa fila, se crea una columna cuyo nombre es la fecha/hora y cuyo valor es la posición. Si más adelante queremos consultar las posiciones de un camión, Cassandra sabrá en qué disco está el camión por su id y cargará la fila completa con todas las fecha/hora y posición.

Y surge ahí otra pega. Una fila en Cassandra está limitada a unos 2Gigas, pero además, como se carga en memoria completa, conviente que la fila completa no ocupe más de la memoria que estemos dispuestos a gastar en ello (unos Megas, posiblemente). Así que debemos "partir" esa fila en filas más pequeñas haciendo ciertos "artificios" extraños. Por ejemplo, la clave de la fila, en vez de ser el camión, podria ser una cosa compuesta como <camion>.<dia>, así en cada fila sólo estarán las posiciones de ese camión en ese día.

Y esta tabla sólo para consultar posiciones de un camión en determinadas fechas. Habría que inventar tablas similares si se quieren consultar, por ejemplo, camiones que hayan estado en una zona  geográfica determinada en determinadas fechas, etc, etc. Y todas esas tablas tenemos que tenerlas actualizadas desde código.

Bueno, el detalle no importa, lo importante es hacerse idea del "artificio" que hay que montar para poder hacer consultas eficientes por una columna concreta con una posición geográcia o con una fecha/hora y que el diseño de tablas va a estar muy relacionado con las consultas que queramos hacer. En muchos sitios, a la hora de diseñar una base de datos Cassandra, aconsejan empezar pensando qué consultas va a hacer nuestra aplicación, y luego empezar a diseñar tablas, incluso repitiendo datos en varias tablas si es neceasario por eficiencia.

¿Y cual es la conclusión?. Si vas a tener "BigData" (cantidades ingentes de datos y conexiones simultáneas), adelante, usa Cassandra o una base de datos equivalente, no te queda más remedio ya que las bases de datos SQL no son capaces. Pero si no vas a tener "BigData", usar Cassandra no te va a aportar grandes beneficios, pero si montones de pegas si no tienes muy claro qué consultas vas a hacer exactamente en tus tablas, y tendrás más cosas que codificar en tu aplicación para facilitar consultas "extrañas", como consultar por fecha/hora, ya que tendrás que hacer los insert adecuados en las tablas de columnas variables con claves compuestas <camion>.<dia>.  Por cierto, ¿he comentado que Cassandra no tiene JOINs ni permite consultas mezclando tablas?. Sí, es otra cosa que tendrás que hacer desde tu código.

Hay otro posible caso en que puede ser útil una base de datos NoSQL (pienso quizás en alguna como MongoDB más que Cassandra) y es en pequeñas/medianas aplicaciones donde sólo se necesite una base de datos y no se requiera toda la complejidad de una base de datos SQL (tablas, restricciones, "foreing keys", etc). Bases de datos como MongoDB son muy dinámicas, podemos insertar datos variopintos sobre la marcha sin necesidad de echar unas horas diseñando tablas y modificándolas si luego no nos valen. Evidentemente, con MongoDB también hay que echar una "pensada" previa a lo que queremos, pero no tenemos la rigidez de una base de datos SQL.