Capítulo 4. Servicios y datos

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Cuando construyas y migres tu aplicación a una arquitectura basada en servicios, es muy importante que tengas en cuenta dónde almacenas los datos y el estado dentro de tu aplicación.

Servicios sin estado-Servicios sin datos

Los servicios sin estado son servicios que no gestionan datos ni estado propios. Todo el estado y todos los datos que el servicio necesita para realizar sus acciones se pasan (o se referencian) en la solicitud enviada al servicio.

Los servicios sin estado ofrecen una gran ventaja para el escalado. Al ser sin estado, suele ser fácil añadir capacidad de servidor adicional a un servicio para escalarlo a una mayor capacidad, tanto vertical como horizontalmente. Obtienes la máxima flexibilidad en cómo y cuándo puedes escalar tu servicio si éste no mantiene el estado.

Además, ciertas técnicas de almacenamiento en caché en el frontend del servicio se hacen posibles si la caché no necesita preocuparse del estado del servicio. Este almacenamiento en caché te permite manejar mayores requisitos de escalado con menos recursos.

No todos los servicios pueden hacerse sin estado, obviamente, pero para los servicios que pueden ser sin estado, es una gran ventaja para la escalabilidad.

Servicios con Estado-Servicios con Datos

Cuando necesites almacenar datos, teniendo en cuenta lo que acabamos de comentar en la sección anterior, podría parecer obvio almacenar los datos en el menor número posible de servicios y sistemas. Podría tener sentido mantener todos tus datos cerca unos de otros para reducir la huella de los servicios que necesitan conocer y gestionar tus datos.

Nada más lejos de la realidad.

En lugar de eso, localiza tus datos tanto como sea posible. Haz que los servicios y almacenes de datos gestionen sólo los datos que necesitan para realizar su trabajo. Los demás datos deben almacenarse en servidores y almacenes de datos distintos, más cerca de los servicios que los necesitan.

Localizar los datos de esta forma tiene algunas ventajas:

Tamaño reducido de los conjuntos de datos individuales

Como tus datos se dividen en conjuntos de datos, cada conjunto de datos es de menor tamaño. Un menor tamaño del conjunto de datos significa una menor interacción con los datos, lo que facilita la escalabilidad de la base de datos. Esto se llama partición funcional. Estás dividiendo tus datos basándote en líneas funcionales y no en el tamaño del conjunto de datos.

Acceso localizado

A menudo, cuando accedes a los datos de una base de datos o de un almacén de datos, accedes a todos los datos de un registro o conjunto de registros determinado. A menudo, muchos de esos datos no son necesarios para una interacción determinada. Al utilizar varios tamaños de conjuntos de datos reducidos, reduces la cantidad de datos innecesarios de tus consultas.

Métodos de acceso optimizados

Al dividir tus datos en diferentes conjuntos de datos, puedes optimizar el tipo de almacén de datos adecuado para cada conjunto de datos. ¿Un conjunto de datos concreto necesita un almacén de datos relacional? ¿O es aceptable un simple almacén de datos clave/valor?

Mantener tus datos asociados a los servicios que los consumen creará una solución más escalable y una arquitectura más fácil de gestionar, y permitirá que tus requisitos de datos se amplíen más fácilmente a medida que se expanda tu aplicación.

Partición de datos

La partición de datos puede significar muchas cosas. En este contexto, significa dividir los datos de un tipo determinado en segmentos basados en alguna clave o identificador dentro de los datos. A menudo se hace para utilizar varias bases de datos para almacenar conjuntos de datos más grandes o conjuntos de datos a los que se accede con una frecuencia mayor de la que puede manejar una sola base de datos.

Existen otros tipos de partición de datos (como la ya mencionada partición funcional); sin embargo, en este apartado, vamos a centrarnos en este esquema de partición basado en claves.

Un ejemplo sencillo de partición de datos es dividir todos los datos de una aplicación por cuentas, de modo que todos los datos de las cuentas cuyo nombre empiece por A-D estén en una base de datos, todos los datos de las cuentas cuyo nombre empiece por E-K estén en otra base de datos, y así sucesivamente (ver Figura 4-1).1 Se trata de un ejemplo muy simplista, pero la partición de datos es una herramienta habitual utilizada por los desarrolladores de aplicaciones para escalar drásticamente el número de usuarios que pueden acceder a la aplicación en un momento dado, así como para escalar el tamaño del propio conjunto de datos.

Example of data partitioning by account name
Figura 4-1. Ejemplo de partición de datos por nombre de cuenta

En general, debes evitar la partición de datos siempre que sea posible. ¿Por qué? Bueno, siempre que particiones datos de esta manera, te encuentras con varios problemas potenciales:

Complejidad de la aplicación

Aumentas la complejidad de tu aplicación porque ahora tienes que determinar dónde están almacenados tus datos antes de poder recuperarlos realmente.

Consultas entre particiones

Eliminas la posibilidad de consultar fácilmente los datos en varias particiones. Esto es especialmente útil para realizar consultas de análisis empresarial.

Uso sesgado de las particiones

Elegir cuidadosamente tu clave de partición es fundamental. Si eliges la clave equivocada, puedes sesgar el uso de las particiones de tu base de datos, haciendo que algunas particiones funcionen más calientes y otras más frías, reduciendo así la eficacia del particionamiento y complicando al mismo tiempo la gestión y el mantenimiento de tu base de datos. Esto se ilustra en la Figura 4-2.

Repartición

En ocasiones es necesario reparticionar para equilibrar eficazmente el tráfico entre particiones. Dependiendo de la clave elegida y del tipo y tamaño del conjunto de datos, puede resultar una tarea extremadamente difícil, extremadamente peligrosa (migración de datos) y, en algunos casos, casi imposible.

En general, el nombre de la cuenta o el ID de la cuenta es casi siempre una mala clave de partición (sin embargo, es una de las claves que se eligen con más frecuencia). Esto se debe a que una misma cuenta puede cambiar de tamaño a lo largo de su vida. Fíjate en la Figura 4-2. Una cuenta puede empezar siendo pequeña y, por tanto, caber fácilmente en una partición con un número significativo de cuentas pequeñas. Sin embargo, si crece con el tiempo, pronto puede hacer que esa única partición no sea capaz de manejar toda la carga adecuadamente, y tendrás que reparticionar para equilibrar mejor el uso de las cuentas. Si una sola cuenta crece demasiado, puede llegar a ser mayor de lo que cabe en una sola partición, lo que hará fracasar todo tu esquema de particionado, porque ningún reequilibrio resolverá ese problema.

Example of accounts overrunning data partitions
Figura 4-2. Ejemplo de cuentas que sobrepasan particiones de datos

Una clave de partición mejor sería la que diera lugar a particiones de tamaño lo más coherente posible. El crecimiento de las particiones debe ser lo más independiente y coherente posible, como se muestra en la Figura 4-3. Si es necesario reparticionar, debe ser porque todas las particiones han crecido de forma coherente y son demasiado grandes para ser manejadas por la base de datos.

Un esquema de partición potencialmente útil es utilizar una clave que genere un número significativo de elementos pequeños. A continuación, mapea estas pequeñas particiones en bases de datos particionadas más grandes. Luego, si es necesario reparticionar, sólo tienes que actualizar la asignación y mover los elementos pequeños individuales a las nuevas particiones, eliminando la necesidad de reparticionar masivamente todo el sistema. Seleccionar y utilizar las claves de partición adecuadas es un arte en sí mismo.

Example of consistently sized partitioned elements
Figura 4-3. Ejemplo de elementos particionados de tamaño coherente

Tratamiento oportuno de los dolores crecientes

La mayoría de las aplicaciones modernas experimentan un crecimiento en sus requisitos de tráfico, en el tamaño y complejidad de las propias aplicaciones y en el número de personas que trabajan en ellas. A menudo, ignoramos estos dolores de crecimiento, esperando a que el dolor alcance un cierto umbral antes de intentar solucionarlo. Sin embargo, para entonces suele ser demasiado tarde. El dolor ha alcanzado un nivel grave, y muchas técnicas fáciles para ayudar a reducirlo ya no están disponibles para que las utilices.

Si no pensamos en cómo puede crecer nuestra aplicación mientras la arquitecturamos antes de que escale, nos encerraremos en decisiones arquitectónicas que pueden bloquear nuestra capacidad de escalar según lo requiera nuestro negocio.

En su lugar, mientras diseñas y arquitecturas tu nueva aplicación y los cambios en tus aplicaciones existentes, considera cómo se verán afectados esos cambios por posibles cambios de escala en el futuro. ¿Cuánto espacio para escalar has construido? ¿Cuál es el primer muro de escalabilidad con el que te toparás? ¿Qué ocurre cuando llegas a ese muro? ¿Cómo puedes responder y eliminar la barrera sin necesidad de una rearquitectura importante de la aplicación?

Si piensas en cómo crecerá tu aplicación mucho antes de que llegue a esos dolorosos niveles, puedes anticiparte a muchos problemas y construir y mejorar tus aplicaciones para que puedan manejar estos dolores de crecimiento con seguridad y protección.

1 Un mecanismo de partición basado en cuentas más probable sería particionar por un identificador de cuenta en lugar de por el nombre de la cuenta. Sin embargo, utilizar el nombre de cuenta hace que este ejemplo sea más fácil de seguir.

Get Arquitectura a escala, 2ª edición now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.