Cada vez las necesidades de rendimiento en nuestros motores relacionales son mayores y ello hace que el uso de estructuras de almacenamiento basadas en disco se convierta en un cuello de botella.

En este artículo vamos a revisar algunas de las alternativas que tenemos en el mercado donde la ubicación natural de nuestros datos es la memoria, siendo el disco usado únicamente como “backup” o para poder proporcionar durabilidad de nuestras transacciones.

Muchos de nuestros clientes tienen ya desarrollos basados en SQL Server por lo que cuando nos plantean necesidades que encajan con las ventajas de las bases de datos en memoria la primera opción que tiene sentido considerar es el uso de In-Memory OLTP dentro del mismo motor SQL Server. La posibilidad de utilizar esta tecnología se “democratizó” desde SQL Server 2016 SP1 cuando se extendió más allá de la versión Enterprise.

¿Realmente qué me ofrece SQL Server?

A diferencia de otras alternativas, SQL Server permite mezclar, incluso en una misma base de datos, tablas en memoria con tablas con almacenamiento “clásico” en disco. Es decir, SQL Server nos permitirá transicionar una parte de nuestra aplicación a un modelo basado en memoria de una forma relativamente sencilla.

La realidad es que muchas bases de datos no necesitan el modelo “in-memory” para la mayor parte de sus tablas sino únicamente para algunas tablas que entran en alguno de los escenarios ideales para esta tecnología. Concretamente los escenarios más adecuados son aquellos que impliquen un procesado de transacciones muy elevado (OLTP “rabioso”), ingestión de datos con mucha concurrencia (sensores, IoT, colas, cachés/amortiguadores) o escenarios de ingestión de datos masiva por volumen (especialmente si hablamos de datos transitorios, como tablas de staging, etc.)

Para poder comparar la alternativa in-memory basada en SQL Server con otros productos que soporten datos relacionales en memoria conviene que revisemos qué tipo de estructuras de datos se utilizan y si ello implica “sacrificar” ciertas características que sí tenemos en el almacenamiento tradicional.

¿Cómo se almacena la información en memoria?

En SQL Server cuando creamos una tabla en memoria lo que estamos creando es una estructura indexada latch-free bien en forma de tabla hash (hash index) o en forma de árbol (range index). Los índices hash son útiles cuando básicamente vamos a utilizar esta tabla para búsquedas de un único registro en base a un valor concreto siendo un caso típico el de una primary key. Los índices de rango gracias a su estructura en formato de árbol nos permiten realizar recorridos de rangos de valores y son por tanto más útiles cuando nuestras consultas necesitan filtrar por un rango (de fechas por ejemplo). Otros motores en memoria utilizan estrategias distintas para el almacenamiento en memoria. Por ejemplo, SingleStore (antes conocido como MemSQL) utiliza una estructura llamada Skip List. La ventaja de una Skip List versus alguna variante de los btrees clásicos es que reduce el overhead necesario que introducen bajo la premisa de que los accesos pueden ser lento (fallo en memoria y accesos a disco). Esto no aplica en bases de datos que van a estar siempre en memoria. En este caso la complejidad algorítmica en el acceso a un dato es O(log(n)) y su mantenimiento es mucho más sencillo ya que no existen procesos de rebalanceo y/o de page split como ocurre en las estructuras basadas en árboles. Al igual que los bw-trees que tenemos en SQL Server, SingleStore utiliza una versión lock free y thread safe de Skip List, de forma que ante concurrencia elevada no tengamos problemas de contención.

Analizando un poco más a fondo VoltDB

Cuando hablamos de bases de datos en memoria debemos necesariamente considerar VoltDB como una de las opciones más relevantes. VoltDB nació en 2010 y es derivada de H-Store que nació en 2007 de un equipo de investigación entre los que destaca el mítico Michael Stonebraker. El objetivo era maximizar el trabajo “útil” o efectivo de la base de datos, que en casos de alta carga, puede llegar a ser en los motores tradicionales una parte pequeña del total:

Bases de datos en memoria, una alternativa cada vez más interesante (Parte 1)

https://downloads.voltdb.com/datasheets_collateral/technical_overview.pdf

Desde sus orígenes se planteó como un modelo en cluster shared nothing donde los “ejecutores” se responsabilizan de un conjunto de datos disjunto (sin solapes). Seguramente a algunos de vosotros estos conceptos os estarán recordando a tecnologías como Spark y la realidad es que al final gran parte de los sistemas distribuidos acaban compartiendo conceptos clave (aunque a veces se les cambien los nombres para darse autobombo).

Otro concepto clave de H-Base y heredado por VoltDB es el uso de operaciones single-thread a nivel de partición y va asociado al concepto de “virtual core”. El uso de esta aproximación hace que en un momento dado únicamente un thread pueda estar interactuando con un dato concreto, lo que evita que tengamos que gestionar el acceso concurrente al dato mediante latches o bloqueos. Seguramente a otras personas esto les recuerde a Redis con su aproximación single-thread que precisamente busca evitar usar ningún sistema de latches/bloqueos.

Es importante destacar que para tener un buen rendimiento y escalabilidad es necesario que nuestro modelo de datos pueda ser particionado de forma que no tengamos “particiones calientes” que acaben siendo el cuello de botella por este procesado single thread. Existen muchos escenarios donde es posible realizar este tipo de particionados, por ejemplo en telecomunicaciones, servidores de juegos online, IoT, etc. pero es importante tenerlo presente en tiempo de diseño.

Un aspecto que comparte VoltDB con otros motores como CosmosDB es que el soporte de transacciones está ligado al uso de procedimientos almacenados. Esto puede parecer extraño en un principio pero tiene que ver con la forma en la que la replicación de las operaciones distribuidas funciona en estos sistemas.

¿Es seguro utilizar VoltDB comparado con los motores tradicionales?

El uso de conjuntos de datos disjuntos y sin solapes cuando hablamos de bases de datos en memoria nos puede resultar algo preocupante de cara a la disponibilidad. Para esos escenarios donde la disponibilidad sea crítica (no tienen que ser todos los sistemas así) VoltDB utiliza K-Safety. Este mecanismo lo que busca es mantener copias de las particiones en varios nodos de forma que en caso de fallo de uno de los nodos el dato siga disponible. Obviamente esta copia extra que se realiza de forma síncrona implica una degradación de rendimiento versus el escenario sin copia redundante.

Un concepto muy similar lo tenemos también en el log de transacciones “in memory” que tiene SingleStore. Este log es replicado a por lo menos dos nodos de forma que salvo fallo simultáneo de dos nodos no perderemos datos. Tanto en el caso de VoltDB como en SingleStore una caída de múltiples nodos simultáneamente o del conjunto de nodos puede ocasionar pérdida de datos por lo que no tenemos la durabilidad asegurada como en los motores de bases de datos tradicionales.

La durabilidad en VoltDB se gestiona por una parte mediante snapshots a disco de la memoria. Esta operación vendría a ser similar al proceso de checkpoint en un motor relacional. Adicionalmente a estos snapshots tendremos un proceso opcional de command logging que básicamente registra las llamadas a procedimientos almacenados realizadas entre snapshots. Este proceso es configurable y puede configurarse que realice el volcado a disco de forma síncrona o asíncrona así como su frecuencia. De nuevo aquí tenemos que buscar un compromiso entre sacrificar durabilidad en caso de fallo y rendimiento.

En el caso de un cluster de VoltDB también podemos tener problemas de tipo splitbrain donde a nivel de networking varios nodos dejan de ver a otros nodos y por tanto las particiones que no estén replicadas en los nodos de la parte “superviviente” del cluster dejarán de estar accesibles. Esto no significa que el dato esté definitivamente perdido, ya que si se recuperan las comunicaciones tenemos un proceso de “reincorporación en vivo” (live node rejoin) al cluster que nos permite volver a unir estos nodos y recuperar así el acceso a esta información que habíamos dejado de tener disponible.

¿Podré migrar mi modelo relacional a VoltDB fácilmente?

Algunas de las limitaciones a nivel de modelado que tenemos en VoltDB cuando lo comparamos con un motor relacional “tradicional” no son banales y debemos tenerlas en cuenta. Por ejemplo no tenemos soporte ni de foreign keys ni de CHECK contraints. Tampoco tenemos soporte de valores autoincrementales, secuencias, etc. Los triggers tampoco están soportados en este motor.

Si definimos un particionado para las tablas (necesario para escalar) debemos incluir la columna de particionado en el índice (limitación habitual en otros motores también). Tampoco tenemos un lenguaje potente para procedimientos basado en SQL (del estilo del disponible en T-SQL, PLSQL, etc) y deberemos usar procedimientos almacenados programados en JAVA como alternativa. Este tipo de limitaciones deben tenerse en cuenta especialmente si nos planteamos una migración desde un motor que sí soporte estas características.

¿Y qué ocurre con los escenarios analíticos y HTAP?

También debemos tener en cuenta que VoltDB no es un motor adecuado para las tareas analíticas. Es por ello que en el producto se han realizado esfuerzos en tener procesos de “exportación” que lo que buscan es ir volcando los datos a otros sistemas para poder realizar la analítica.

automated control

http://www.odbms.org/wp-content/uploads/2013/11/VoltDBTechnicalOverview.pdf

En este sentido SingleStore tiene un enfoque distinto ya que busca, usando almacenamiento columnar dentro del propio producto, permitir ese escenario HTAP. En la última versión 7.5 se ha liberado el que han denominado modelo “universal storage” que mezcla almacenamiento columnar con indexación tradicional inmemory para poder servir ambas necesidades sin necesidad de copiar ni transformar los datos. En un futuro se mejorará aún más con un buffer en memoria específico para los columnares ya que actualmente se confía esta labor a la caché a nivel de ficheros del OS (al estilo PostgreSQL).

Por otra parte tenemos ciertas funcionalidades especializadas en VoltDB que no tenemos en otros motores y que pueden facilitarnos ciertas tareas. Por ejemplo, si tenemos ciertos datos que son volátiles por naturaleza podemos definir un TTL automático en la tabla y hacer que VoltDB borre automáticamente los datos a medida que envejezcan. O también podemos archivar los datos cuando expire el TTL a otra tabla de archivado. Como podéis ver son tareas bastante específicas orientadas al tipo de problemas que VoltDB busca solucionar.

¿Es buena idea para mi sistema plantearme una base de datos en memoria?

Por resumir un poco lo visto, consideramos que una de las características comunes de estos motores es que buscan una escalabilidad horizontal y un uso inmemory prioritario. Debido a ello la distribución de las tablas se necesita realizar mediante técnicas de particionado para escalar a la vez la capacidad de procesamiento y de memoria. En ambos casos si queremos sobrevivir a fallos a nivel de nodo los datos o el log de transacciones deberá estar replicados en N nodos. Por tanto en función del escenario tendremos que decidir si estamos dispuestos asumir más riesgos a cambio de rendimiento o queremos más durabilidad.

No olvidemos que ambos casos que hemos comentado, SingleStore y VoltDB, son opciones que tienden más a la especialización, a resolver problemas concretos que los motores relacionales con almacenamiento on-disk no resuelven de forma efectiva. Si observamos su popularidad vemos que tanto VoltDB (línea roja) como SingleStore (línea amarilla) siguen siendo productos nicho, con muy poco uso comparado con Oracle, DB2, SQL Server, MySQL, PostgreSQL, etc. Es cierto que se aprecia una tendencia favorable a SingleStore versus VoltDB en los últimos 5 años y es algo que debemos tener en cuenta:

 

Bases de datos en memoria, una alternativa cada vez más interesante (Parte 1)

Por tanto antes de decidir realizar una migración a este tipo de motores debemos tener claro que es lo que realmente necesitamos, ya que en nuestra experiencia en la mayoría de casos una buena ración de tuning/troubleshooting sobre un motor relacional “clásico” como SQL Server, PostgreSQL, Oracle, etc. puede dar resultados sorprendentes a la hora de resolver problemas típicos de rendimiento.

Para no quedarnos con la parte teórica en la segunda parte de este artículo veremos cómo montar un pequeño cluster con VoltDB sobre contenedores y también realizaremos algunas pruebas de rendimiento para tener ciertas referencias sobre qué podemos esperar de este tipo de sistemas comparadas con un motor transaccional clásico.

Conclusión

A día de hoy apostar por una base de datos relacional 100% en memoria implica en muchos casos de prescindir de ciertas funcionalidades a cambio de un mayor rendimiento en escenarios concretos. Creemos que realmente el boom de esta tecnología vendrá de la mano del aumento de la disponibilidad de memoria persistente ya que permitirá maximizar las ventajas de las bases de datos en memoria y eliminar o minimizar muchas de sus restricciones en lo que respecta a la durabilidad o a la tolerancia a fallos sin pérdida de datos.

¡Sigue Aprendiendo!

Si quieres que te ayudemos a poner en marcha este tipo de proyecto, no dudes en contactar con nosotros. También puedes aprovechar las formaciones existentes para ampliar tus conocimientos 👇🏼

>> Más info
0 Shares:
Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like