Usar o no usar una base de datos

Durante unos cuantos años la base de datos que más utilicé fue SQL Server CE. En aquella época (finales de los 2000) me dedicaba a desarrollar aplicaciones móviles para equipos con Windows CE y esa era la base de datos. Tenía sus limitaciones, pero funcionaba relativamente bien. Soportaba gran parte de la sintaxis de SQL Server convencional, era razonablemente rápida si no necesitabas almacenar volúmenes de datos muy grandes y, en general, era estable.

Aun así, llegó un momento en que empezó a resultarme incómoda. Estaba empezando a utilizar modelos de dominio más complejos, había conocido las ventajas de usar un ORM y tener que lidiar con la impedancia entre un modelo rico de objetos y un modelo relacional se me hacía un poco cuesta arriba.

Todo eso me llevó a plantearme si realmente necesitaba una base de datos como tal, o si podría vivir con algún otro sistema de persistencia, y en una de las últimas aplicaciones de aquel estilo que desarrollé, opté por no emplear una base de datos en el dispositivo móvil.

El dominio de la aplicación no es importante para el post, pero para ponernos un poco en contexto, era la típica aplicación móvil en la que existe un servidor, se descargan ciertos datos, se trabaja sobre ellos (posiblemente desconectados del servidor), y se van sincronizando periodicamente con el servidor. Hoy en día, con la ubicuidad de servicios online, la mayoría de aplicaciones (móviles o no) se acaban ajustando a un esquema similar.

La libertad

Eliminar la base de datos relacional de la ecuación suponía tener mucha más libertad a la hora de modelar una solución.

Podríamos haber optado por mantener en memoria la información que se descargaba del servidor.

Esa es una solución sencilla y viable, si estás dispuesto a perder la información, tanto descargada del servidor como generada localmente, en caso de que la aplicación se reinicie. ¿Suena raro? Es lo que hacen la mayoría de las páginas web. Si estás introduciendo datos y refrescas la página, los datos se pierden. Y muchas veces eso no es crítico.

Si la interacción con el servidor es lo bastante frecuente y fiable como para que nunca haya demasiada información introducida por el usuario susceptible de ser perdida, y recuperar del servidor la información necesaria para trabajar (datos maestros, configuración, etc.) es suficientemente rápido, podemos optar por evitar todo tipo de almacenamiento persistente local y mantener la información en memoria.

Naturalmente, cuando el usuario tiene que dedicar más esfuerzo a introducir información, o cuando puede pasar mucho tiempo hasta persistir los datos de forma segura en el servidor, limitarse a guardar la información en memoria suena peor. El riesgo de perder información aumenta, y pocas cosas molestan más a un usuario que perder su información.

Lo mismo ocurre cuando descargar los datos necesarios para empezar a trabajar es muy costoso y no podemos permitirnos hacerlo cada vez que arranca la aplicación, o necesitamos poder hacerlo cuando la aplicación arranca sin posibilidad de conectarse al servidor.

Ante este escenario, tendremos recurrir a algún tipo de almacenamiento persistente y almacenar la información en él.

Sólo tenemos que ser capaces de serializar y deserializar nuestro modelo y ya está. Así podemos guardar directamente objetos en la «base de datos», es decir, en el sistema de archivos, y hemos resuelto el problema de la impedancia entre dos modelos, entre el relacional y el de objetos.

Existen sistemas de serialización muy rápidos y compactos que hacen que podamos aplicar esta técnica sin preocuparnos demasiado de optimizar el acceso a disco, dando lugar a una solución simple para nuestro problema.

Viéndolo así, suena atractivo. En lugar de depender de una base de datos, podemos limitarnos a guardar en disco nuestros objetos y recuperarlos fácilmente cuando los necesitemos. Te ahorras una dependencia, aprender un lenguaje de consultas, y, en el caso de base de datos relacionales, tener que convertir entre dos modelos diferentes.

La responsabilidad

Tengo que reconocer que el plan de trabajar sin una base de datos «de verdad» funcionó bien, pero también debo decir que hubo unas cuantas cosas en las que no habíamos pensado de antemano y que hay que tener en cuenta cuando decides que te vas a encargar tú mismo de guardar la información.

¿Alguna vez has oido hablar de las propiedades ACID referidas a una base de datos? Pues resulta que conseguir que tu almacenamiento cumpla (o al menos se acerque a cumplir) esas propiedades dista mucho de ser trivial. El impacto que tiene en la aplicación depende del tipo de aplicación y de lo dispuesto que estés a asumir problemas variados, pero en general no es despreciable.

La mayoría de sistemas de archivos en los que vas a guardar la información no son transaccionales. Esto quiere decir que si tu información se almacena en más de un fichero eres tú el responsable de garantizar que si una operación afecta a más de uno, todos los ficheros quedan modificados en disco o no se modifica ninguno. ¿Qué vas a hacer si la aplicación se rompe mientras está escribiendo? ¿Y si el sistema se queda sin batería? ¿Y si falla una operación de escritura?

Esta es la Atomicidad de ACID. Una forma de intentar minimizar este problema es tratar de que las operaciones afecten a un único fichero cada vez. La atomicidad, junto a la Consistencia de ACID, también se manifiesta a la hora de realizar operaciones en memoria.

Cuando trabajas con una base de datos transaccional, el modo más habitual consiste en cargar datos en memoria, operar con ellos, y hacer el commit cuando has terminado. Si algo falla, se hace un rollback de la transacción y la siguiente vez que vayas a usar esos datos volverás a cargar datos válidos de la base de datos.

Si en lugar de cargar datos nuevos cada vez que vas a iniciar una operación, mantienes los datos en memoria, cuando se produce un problema durante una operación necesitas simular el rollback en memoria para garantizar que tu información no se queda en estado inconsistente.

Puedes evitar esto si utilizas un sistema de memoria transaccional. Usar estructuras de datos inmutables también ayuda, y determinados lenguajes incluyen construcciones, como los atoms de clojure que resultan útiles.

Si tu aplicación realiza operaciones concurrentes, algo que por desgracia es mucho más frecuente de lo que nos gustaría, te va a tocar pensar en la I de aislamiento (Isolation) de ACID. Si, por ejemplo, tienes una hebra en segundo plano que actualiza datos que va descargando de un servidor, tendrás que garantizar que la hebra que usa esos datos para pintar en pantalla no pinta información a medio actualizar. Lo mismo ocurre si hay datos que tienes que ir enviando al servidor en segundo plano.

Aquí toca echar mano de todas las típicas herramientas de control de concurrencia y (semáforos, locks, señales, inmutabilidad, etc.).

Poniéndonos rigurosos, la Durabilidad de ACID es seguramente la parte más complicada de conseguir, incluso para base de datos «de verdad». Con la cantidad de capas intermedias que existen entre la aplicación y el almacenamiento persistente (ya sea HD, SSD, etc.), y lo variopinta que es la implementación de determinados estándares por parte de los fabricantes, garantizar que cuando algo se escribe en disco realmente está escrito en disco y no perdido en alguna caché intermedia es muy complicado. Aun así, las bases de datos «de verdad» intentan mejorar la situación asegurándose que los buffers del sistema operativo se vuelvan adecuadamente a disco y usando técnicas parecidas que no suelen emplearse en aplicaciones normales.

Otra cosa que descubrimos por las malas es que las bases de datos están muy optimizadas. Había consultas que filtraban conjuntos de datos grandes, e incluso teniendo que acceder a disco, SQL Server CE era capaz de hacerlas más rápido que nosotros en memoria, porque estaban empleando mejores algoritmos y contaban con las estructuras de datos adecuadas (como índices) para optimizarlos.

Y todo esto sin contar con la parte de tooling. Herramientas para hacer copias de seguridad, para inspeccionar los datos almacenados, para monitorizar el comportamiento… En nuestro caso nada de eso era necesario, pero en aplicaciones más complejas sí puede serlo.

Conclusión

Visto en perpectiva, me alegro de no haber usado SQL Server CE en aquella aplicación. La aplicación funcionó bien, nunca tuvimos problemas serios con la persistencia y aprendimos mucho por el camino.

Desarrollar sin tener que pensar cómo íbamos a mapear nuestros objetos a tablas relacionales fue muy cómodo y nos ayudó a iterar rápido en un proyecto en el que lo necesitábamos especialmente.

Pero sobre todo, por el camino comprendimos todo lo que te da una base de datos más allá del paradigma que use (relacional, documental, orientada a objetos). Tener que pensar en todos los problemas potenciales que podían surgir (accesos concurrentes, caídas de la aplicación, fallos de disco) y cómo íbamos a responder a ellos supuso un trabajo adicional que no sé si al final mereció la pena.

A día de hoy, probablemente no renunciaría a utilizar alguna base datos sólida sobre la que construir la aplicación si la plataforma me lo permite. Aunque sólo sea para utilizarla como sistema de archivos transaccional y acabara guardando blobs en json o xml, creo que muchas veces merece la pena.

7 comentarios en “Usar o no usar una base de datos

  1. Este artículo me sirve muchísimo para entender un poquito más las diferencias en desarrollo real entre las bases de datos relaciones y las no relacionales, ¿hay algún blog que trate en profundidad este tipo de problemáticas?.

  2. Me temo que no conozco ninguno que trate de eso en concreto.

    De todas formas, lo que comento no es tanto una cuestión de si la base de datos es relacional o no, como de las características que tiene la base de datos. Hay bases de datos NoSQL que son ACID y bases de datos relacionales que no lo son.

  3. Sí, puede que me haya expresado o que lo haya entendido mal, base de datos lo estoy estudiando actualmente y estoy tratando de tener claras estas cosas, que muchas veces me confundo.

    Ni que decir que me abrís la cabeza con esto de que haya bases de datos NoSQL que sean ACID, tengo que leer mucho más sobre eso porque ‘en los papeles’ todo me decía que no.

    Cuánto más leo más me doy cuenta de lo extenso que es este mundo, pero extrañamente -porque para mí era un tema aburrido- lo estoy encontrando adictivo.

    ¡Saludos!.

  4. Pregunton Cojonero dijo:

    «Hay bases de datos NoSQL que son ACID y bases de datos relacionales que no lo son.» ? Ejemplos ?

    Sobre el artículo… añadir SQLite, Microsoft Sync Framework … ?

  5. Yoandre Saavedra dijo:

    Este blog se ha convertido en mi desayuno informativo en las mañanas. Felicidades Juan Maria!!!

  6. Muchas gracias, me alegro de que te resulte útil. Ahora escribo menos, pero hay mucho material antiguo que está más ligado a conceptos que ha tecnologías de moda que puede seguir siendo útil.

Comentarios cerrados.