¿Cómo manejar las restricciones de clave externa cuando se migra de monolito a microservicios?

18

Mi equipo está migrando de una aplicación ASP.NET monolítica a .NET Core y Kubernetes. Los cambios en el código parecen estar yendo tan bien como se puede esperar, pero el lugar donde mi equipo está encontrando mucha discordia está en la base de datos.

Actualmente tenemos una base de datos de SQL Server bastante grande que alberga todos los datos de todo nuestro negocio. Propongo que dividamos la base de datos de una manera similar a la división del código: datos de catálogo en una base de datos (lógica), datos de inventario en otra, pedidos en otra, etc., y cada microservicio sería el guardián de su base de datos. .

La implicación aquí es que las claves foráneas que cruzan los límites del microservicio tendrían que eliminarse y se prohibirían los sprocs y las vistas que cruzan los límites. Todos los modelos de datos pueden o no residir en la misma base de datos física, pero incluso si lo hacen, no deberían interactuar entre sí directamente. Los pedidos aún pueden hacer referencia a los elementos del catálogo por Id, pero la integridad de los datos no se aplicaría estrictamente a nivel de la base de datos y esos datos deberán unirse en código en lugar de en SQL.

Veo la pérdida de estos como compensaciones necesarias para pasar al microservicio y obtener los beneficios de escalabilidad que conlleva. Mientras elijamos nuestras costuras sabiamente y desarrollemos alrededor de ellas, entonces debería estar bien. Otros miembros del equipo insisten en que todo debe permanecer en la misma base de datos monolítica para que todo pueda ser ACID y tener integridad referencial preservada en todas partes.

Esto me lleva a mi pregunta. Primero, ¿es plausible mi postura sobre las restricciones de clave externa y la unión? Si es así, ¿alguien tiene conocimiento de algún material de lectura creíble que pueda ofrecer a mis colegas? Su posición es casi religiosa y no parecen ser influenciados por nada menos que el propio Martin Fowler diciéndoles que están equivocados.

Raymond Saltrelli
fuente
55
La integridad referencial es extremadamente valiosa. ¿Es la escala de la base de datos realmente el cuello de botella aquí? ¿Realmente necesita escalabilidad de estilo microservicio? Usted sabe mejor que yo si ese cambio en la arquitectura es apropiado para su organización, pero tenga en cuenta que simplemente no es una buena opción para muchos casos de uso. Puede haber otras formas de escalar con compensaciones más atractivas. Por ejemplo, si las consultas de la base de datos por segundo son demasiado altas, tal vez la replicación de la base de datos sea todo lo que se necesita. Y puede escalar horizontalmente los servidores web sin tener que usar microservicios.
amon
Buenos puntos. Estamos estudiando algunas de esas opciones para obtener ganancias a corto plazo. Sin embargo, el paso a los microservicios es el juego largo. En mi opinión, nos permitirá escalar durante años en lugar de meses.
Raymond Saltrelli
3
Estoy seguro de que sus clientes estarán encantados de que el pedido que hicieron 0.05ms más rápido se cancele porque alguien más ordenó el mismo producto cuando solo quedaba uno en stock.
Andy
@amon Haz de esto una respuesta y la votaré. Esta es una buena pregunta y los pros y los contras deben estar representados de manera justa.
mcottle
@mcottle ok, listo!
amon

Respuestas:

19

No existe una solución clara porque esto depende completamente de su contexto, en particular, según qué dimensiones se supone que su sistema debe escalar y cuáles son sus problemas reales. ¿Es la base de datos realmente su cuello de botella?

Esta respuesta (desafortunadamente bastante larga) se leerá un poco como "¡los microservicios son malos, monolitos para toda la vida!", Pero esa no es mi intención. Mi punto es que los microservicios y las bases de datos distribuidas pueden resolver varios problemas, pero no sin tener algunos problemas propios. Para presentar un argumento sólido para su arquitectura, debe demostrar que estos problemas no se aplican, pueden mitigarse y que esta arquitectura es la mejor opción para las necesidades de su negocio.

Los datos distribuidos son difíciles.

La misma flexibilidad que permite una mejor escala es la otra cara de las garantías más débiles. En particular, los sistemas distribuidos son mucho más difíciles de razonar.

Las actualizaciones atómicas, las transacciones, la coherencia / integridad referencial y la durabilidad son extremadamente valiosas y no se deben renunciar precipitadamente. No tiene mucho sentido tener datos si están incompletos, desactualizados o completamente incorrectos. Cuando tiene ACID como un requisito comercial pero está utilizando tecnología de base de datos que no puede ofrecerla de inmediato (por ejemplo, muchas bases de datos NoSQL o una arquitectura DB por microservicio), su aplicación debe llenar el vacío y proporcionar esas garantías.

  • Esto no es imposible de hacer, pero es difícil hacerlo bien. Muy engañoso. Especialmente en un entorno distribuido donde hay múltiples escritores para cada base de datos. Esta dificultad se traduce en una alta probabilidad de errores, posiblemente incluyendo datos caídos, datos inconsistentes, etc.

    Por ejemplo, considere leer los análisis de Jepsen de sistemas de bases de datos distribuidos bien conocidos , quizás comenzando con el análisis de Cassandra . No entiendo la mitad de ese análisis, pero el TL; DR es que los sistemas distribuidos son tan difíciles que incluso los proyectos líderes de la industria a veces se equivocan, en formas que pueden parecer obvias en retrospectiva.

  • Los sistemas distribuidos también implican un mayor esfuerzo de desarrollo. Hasta cierto punto, existe una compensación directa entre los costos de desarrollo o la caída de dinero en hardware más robusto.

Ejemplo: referencias colgantes

En la práctica, no debe mirar a la informática sino a los requisitos de su negocio para ver si ACID se puede relajar y cómo. Por ejemplo, muchas relaciones de clave externa pueden no ser tan importantes como parecen. Considere una relación producto - categoría n: m. En un RDBMS podríamos usar una restricción de clave externa para que solo los productos existentes y las categorías existentes puedan ser parte de esa relación. ¿Qué sucede si presentamos servicios separados de productos y categorías, y se elimina un producto o categoría?

En este caso, eso podría no ser un gran problema y podemos escribir nuestra aplicación para que filtre cualquier producto o categoría que ya no exista. ¡Pero hay compensaciones!

  • Tenga en cuenta que esto puede requerir un nivel de aplicación JOINsobre múltiples bases de datos / microservicios, que simplemente mueve el procesamiento del servidor de la base de datos a su aplicación. Esto aumenta la carga total y tiene que mover datos adicionales a través de la red.

  • Esto puede alterar la paginación. Por ejemplo, solicita los siguientes 25 productos de una categoría y filtra los productos no disponibles de esa respuesta. Ahora su aplicación muestra 23 productos. En teoría, ¡una página con cero productos también sería posible!

  • Deberá ejecutar ocasionalmente un script que limpie las referencias colgantes, ya sea después de cada cambio relevante o en intervalos regulares. Tenga en cuenta que tales scripts son bastante caros porque tienen que solicitar cada producto / categoría de la base de datos / microservicio de respaldo para ver si todavía existe.

  • Esto debería ser obvio, pero para mayor claridad: no reutilice las ID. Las ID de estilo de aumento automático pueden o no estar bien. Los GUID o hashes le brindan más flexibilidad, por ejemplo, al poder asignar una ID antes de que el elemento se inserte en una base de datos.

Ejemplo: órdenes concurrentes

Ahora considere una relación producto-orden. ¿Qué sucede con un pedido si un producto se elimina o cambia? Bien, simplemente podemos copiar los datos relevantes del producto en la entrada del pedido para mantenerlos disponibles, intercambiando espacio en disco por simplicidad. Pero, ¿qué sucede si el precio del producto cambia o el producto deja de estar disponible justo antes de que se realice un pedido de ese producto? En un sistema distribuido, los efectos tardan en propagarse y es probable que el pedido se procese con datos obsoletos.

Nuevamente, cómo abordar esto depende de los requisitos de su negocio. Tal vez el pedido desactualizado sea aceptable, y luego puede cancelar el pedido si no se puede cumplir.

Pero tal vez esa no sea una opción, por ejemplo, para configuraciones altamente concurrentes. Considere que 3000 personas se apresuran a comprar entradas para conciertos en los primeros 10 segundos, y supongamos que un cambio en la disponibilidad requerirá 10 ms para propagarse. ¿Cuál es la probabilidad de vender el último boleto a varias personas? Depende de cómo se manejen esas colisiones, pero usando una distribución de Poisson con λ = 3000 / (10s / 10ms) = 3nosotros tenemos una P(k > 1) = 1 - P(k = 0) - P(k = 1) = 80%posibilidad de colisión por intervalo de 10 ms. Si es posible vender y luego cancelar la mayoría de sus pedidos sin cometer fraude, puede llevar a una conversación interesante con su departamento legal.

El pragmatismo significa elegir las mejores características.

La buena noticia es que no tiene que pasar a un modelo de base de datos distribuida, de lo contrario no es necesario. Nadie revocará su membresía al Club de microservicios si no realiza microservicios "correctamente", porque no existe tal club, y no hay una forma real de crear microservicios.

El pragmatismo gana cada vez, así que combina y combina varios enfoques a medida que resuelven tu problema. Esto incluso podría significar microservicios con una base de datos centralizada. Realmente, no pase por el dolor de las bases de datos distribuidas si no es necesario.

Puede escalar sin microservicios.

Los microservicios tienen dos beneficios principales:

  • El beneficio organizacional de que pueden ser desarrollados e implementados independientemente por equipos separados (lo que a su vez requiere que los servicios ofrezcan una interfaz estable).
  • El beneficio operativo de que cada microservicio se puede escalar de forma independiente .

Si no se requiere una escala independiente, los microservicios son mucho menos atractivos.

Un servidor de base de datos ya es un tipo de servicio que puede escalar (algo) de forma independiente, por ejemplo, agregando réplicas de lectura. Usted menciona procedimientos almacenados. Reducirlos podría tener un efecto tan grande que cualquier otra discusión de escalabilidad sea discutible.

Y es perfectamente posible tener un monolito escalable que incluya todos los servicios como bibliotecas. Luego puede escalar iniciando más instancias del monolito, lo que, por supuesto, requiere que cada instancia sea apátrida.

Esto tiende a funcionar bien hasta que el monolito es demasiado grande para ser implementado razonablemente, o si algunos servicios tienen requisitos de recursos especiales, por lo que es posible que desee escalarlos de forma independiente. Los dominios problemáticos que involucran recursos adicionales podrían no involucrar un modelo de datos separado.

¿Tienes un fuerte caso de negocios?

Es consciente de las necesidades comerciales de su organización y, por lo tanto, puede crear un argumento para una arquitectura de base de datos por microservicio, basándose en un análisis:

  • que se requiere una cierta escala, y esta arquitectura es el enfoque más rentable para obtener esa escalabilidad, teniendo en cuenta el mayor esfuerzo de desarrollo para dicha configuración y soluciones alternativas; y
  • que los requisitos de su negocio permiten que las garantías ACID relevantes se relajen, sin generar varios problemas como los discutidos anteriormente.

Por el contrario, si no puede demostrar esto, en particular si el diseño de la base de datos actual es capaz de soportar una escala suficiente en el futuro (como parecen creer sus colegas), entonces también tiene su respuesta.

También hay un gran componente YAGNI para la escalabilidad. Ante la incertidumbre, es una decisión comercial estratégica para construir escalabilidad ahora (costos totales más bajos, pero implica costos de oportunidad y puede no ser necesario) en lugar de diferir algo de trabajo sobre escalabilidad (costos totales más altos si es necesario, pero tiene una mejor idea de la escala real). Esto no es principalmente una decisión técnica.

amon
fuente
Excelente respuesta, gracias. ¿Puedes dar más detalles sobre esta afirmación? ¿Quiere decir que reducir la cantidad de procedimientos podría tener un gran efecto en el rendimiento? Usted menciona procedimientos almacenados. Reducirlos podría tener un efecto tan grande que cualquier otra discusión de escalabilidad sea discutible.
alan
1
@alan Los procedimientos almacenados se pueden usar para siempre, pero plantean dos problemas de rendimiento: (1) Las consultas más complejas son más difíciles de optimizar para la base de datos. (2) Usar sprocs significa hacer más trabajo en el servidor DB. OP quiere dividir la base de datos para escalar aún más, pero evitar complicados sprocs ya podría proporcionar ese margen. Por supuesto, los sprocs y las consultas complejas también pueden ser buenos para el rendimiento, por ejemplo, cuando minimizan la cantidad de datos que deben transferirse fuera de la base de datos para una respuesta de consulta. Dividir la base de datos empeoraría ese problema cuando se necesiten uniones entre servidores.
amon
0

Creo que ambos enfoques son plausibles. Puede elegir ganar escalabilidad sacrificando los beneficios de ACID y las bases de datos monolíticas, así como mantenerse con la arquitectura actual y sacrificar la escalabilidad y la agilidad de una arquitectura más distribuida. La decisión correcta vendrá del modelo comercial actual y de la estrategia comercial para los próximos años. Puramente desde el punto de vista de la tecnología, existen dificultades para mantenerlo monolítico, así como para avanzar hacia un enfoque más distribuido. Analizaría el sistema y vería qué aplicaciones / módulos / procesos de negocio son más críticos para escalar y evaluar los riesgos, costos y beneficios para decidir cuáles deberían esperar o continuar en la arquitectura monolítica.

brunofl
fuente
-1

Su postura es plausible y correcta.

Sin embargo, cómo convencer a los adictos teñidos de lana db es otra cuestión. Yo diría que tienes dos opciones.

  1. Encuentre un ejemplo concreto donde el DB ha alcanzado sus límites. ¿Tienes 'tablas de archivo' por ejemplo? ¿Por qué está bien eso? ¿Cuál es el número máximo de pedidos por segundo que puede tomar? etc. Demuestre que la base de datos no cumple con los requisitos y su solución los corrige.

  2. Contrata contratistas costosos para que te cuenten la mejor solución. Porque son gastos y tienen blogs, todos los creerán

Ewan
fuente
1
No soy el -1, pero para que esta sea una buena respuesta, perdería el esbozo del punto 2 y ampliaría cuándo sería apropiado necesitar múltiples bases de datos. No creo que las tablas de archivo sean necesariamente un antipatrón en las bases de datos que no admiten particiones. La base de datos que estoy usando actualmente tiene alrededor de 130 GB de datos con 26 tablas> 10 millones de filas y el rendimiento no es un problema lo suficientemente remoto como para necesitar dividir la base de datos; Así que soy muy escéptico y me gustaría saber por qué es una buena idea, y cuándo debe hacerse, esta respuesta es la más cercana que he visto hasta ahora.
mcottle
bien. Menciono las tablas de archivo porque anulan las restricciones de FK. Es una grieta en la armadura. dividir por microservicio no es un tamaño de cosa db es mantener sus microservicios separables. Si no puede apagar uno y tirarlo, no es realmente un microservicio. En cuanto al punto 2. el OP menciona a MF, literalmente podrían contratarlo / a para que venga y les diga que dividan la base de datos
Ewan
"Si no puede apagar uno y tirarlo, no es realmente un microservicio". Eso es cierto del servicio en sí, pero no necesariamente un argumento de por qué el servicio necesita su propia base de datos. En última instancia, la base de datos es en sí misma un servicio utilizado por el microservicio. El microservicio realmente no sabe ni le importa si los datos que está utilizando están en una base de datos separada o en una base de datos compartida. Puede girar copias hacia arriba o hacia abajo de este microservicio y nada cambia realmente.
Chris Pratt
El mejor argumento para la base de datos por servicio son los límites de conexión. No es raro que se use la agrupación de conexiones, por lo que cada microservicio ya requiere múltiples conexiones a la instancia de la base de datos, entonces podría tener múltiples instancias de cada uno de estos microservicios, cada uno con sus propias agrupaciones. Eventualmente, las cosas podrían llegar a un punto crítico, donde simplemente ha agotado la capacidad de la base de datos para manejar todas las conexiones que está obteniendo.
Chris Pratt