Base teórica de las transacciones distribuidas

Hoy, en este blog, aprendamos sobre uno de los aspectos clave y difíciles de los microservicios: las transacciones distribuidas.

Aprenderemos basándonos en el framework de Seata.

1. Problema de transacciones distribuidas

Deberíamos tener una mejor comprensión de las transacciones. Sabemos que todas las transacciones deben cumplir con los principios ACID. Eso es

imagen-20230328192513554

En la arquitectura monolítica que estudiamos antes, este servicio accede directamente a una base de datos y el negocio es relativamente simple. Según las características de la propia base de datos, ACID ya se puede implementar.

Sin embargo, lo que vamos a ver ahora son los microservicios. Las empresas de microservicios suelen ser más complejas: una empresa puede abarcar varios servicios y cada servicio tendrá su propia base de datos.

En este momento, si confía en las características de la base de datos en sí, ¿aún puede garantizar el ACID de todo el negocio? Este no es necesariamente el caso.

Veamos un ejemplo.

imagen-20230328192752165

Por ejemplo, aquí tengo un microservicio que contiene tres servicios, incluidos pedidos, cuentas e inventario.

Ahora, tenemos una empresa donde el usuario realiza un pedido. Cuando el usuario realiza un pedido, quiero que el servicio de pedidos cree el pedido y lo escriba en la base de datos.

Luego, llama al servicio de cuenta y al servicio de inventario, el servicio de cuenta deduce el saldo del usuario y el servicio de inventario deduce el inventario de productos.

Luego puede ver que este negocio contiene llamadas a tres microservicios diferentes y cada microservicio tiene su propia base de datos independiente, que es una transacción independiente.

Entonces, lo que finalmente esperamos es que una vez que se ejecute mi pedido, ¿todos los pedidos tendrán éxito? Por supuesto, si tú fracasas, ¿fallan todos?

1.1 Demostración de problemas de transacciones distribuidas

El resultado es ese resultado, pero ¿se puede lograr tal efecto? Comprobémoslo.

La estructura del microservicio es la siguiente:

imagen-20230328194429179

en:

seata-demo: proyecto principal, responsable de gestionar las dependencias del proyecto

  • servicio de cuenta: Servicio de cuenta, responsable de administrar las cuentas de capital de los usuarios. Proporcionar una interfaz para deducir el saldo.
  • servicio-almacenamiento: Servicio de inventario, responsable de gestionar el inventario de productos. Proporcionar una interfaz para deducir el inventario.
  • order-service: Servicio de pedidos, responsable de gestionar los pedidos. Al crear un pedido, es necesario llamar al servicio de cuenta y al servicio de almacenamiento.

Primero echemos un vistazo a este pedido (servicio de pedidos). Eche un vistazo a la lógica de negocios que contiene: en el controlador podemos ver una interfaz para crear pedidos.

imagen-20230328195453369

En el controlador ajusta el servicio, entonces ingresamos el método de servicio.

imagen-20230328203507452

En este método de servicio, podemos ver que primero crea un pedido y escribe los datos del pedido directamente en la base de datos.

Después de crear el pedido, llame a accountClient (deducir el saldo del usuario) y a StorageClient (deducir el inventario) para completar la deducción del saldo y el inventario.

accountClient y StorageClient se llaman a través de Feign.

imagen-20230328200505074

Aquí están los datos de estas tres tablas.

La lógica empresarial es así, usemos Postman para probarla a continuación.

imagen-20230328202949554

Enviamos una solicitud.

imagen-20230328204822270

Se devuelve 500, veamos la base de datos.

imagen-20230328204905021

Se descubre que el saldo del usuario es 200 menos.

imagen-20230328204924437

El pedido no fue creado.

imagen-20230328204940524

Los inventarios tampoco están disminuyendo.

¿Es consistente el estado de la transacción en este momento? ¿No es así?

Entonces, ¿por qué es así?

En el negocio hace un momento, nuestro servicio de pedidos creó un pedido.

Luego llame al servicio de contabilidad y al servicio de inventario para completar la deducción del saldo y la deducción del inventario.

Entre ellos, se han ejecutado con éxito los servicios de pedidos y contabilidad.

imagen-20230328205156568

Sin embargo, cuando se ejecutó el servicio de inventario, se reportó un error por inventario insuficiente.

imagen-20230328205213745

En teoría, si se informa un error aquí, ¿no debería revertirse todo lo demás? Pero el resultado que vemos es que el servicio de inventario falló ¿Se debe descontar o descontar el saldo de la cuenta? ¿por qué?

Primero, cada uno de nuestros servicios es independiente.

Entonces ahora lanzas una excepción en el servicio de inventario.

¿El servicio de cuentas lo sabe? ¡No lo sabe!

Entonces ni siquiera sé que lanzaste una excepción, entonces, ¿qué debo revertir?

En segundo lugar, cada servicio es independiente, por lo que sus asuntos también lo son.

Ahora, para mi servicio de pedidos y servicio de contabilidad, después de finalizar el negocio y mi transacción, ¿debo enviarlos directamente?

Ahora me pides que retroceda, ¿cómo hago para retroceder? Terminé todo y lo envié, pero no puedo deshacerlo.

Entonces, al final, el estado de la transacción no era consistente, ¡y en ese momento apareció el problema de las transacciones distribuidas!

1.2 ¿Qué es una transacción distribuida?

Entonces resumamos qué es una transacción distribuida.

Un negocio bajo sistema distribuido, que abarca múltiples servicios y fuentes de datos. Cada servicio puede considerarse como una transacción de sucursal y lo que queremos garantizar es que el estado final de todas las transacciones de sucursal sea consistente.

O todos triunfan o todos fracasan. Entonces dicha transacción es una transacción distribuida.

Entonces, ¿por qué hay problemas con las transacciones distribuidas?

Esto se debe a que los distintos servicios o las transacciones de las distintas sucursales no se conocen entre sí y, si cada uno envía sus propios asuntos, no podrán retroceder en el futuro, lo que dará lugar a estados inconsistentes.

2. Base teórica

A continuación entraremos en el estudio de la base teórica de las transacciones distribuidas.

Resolver problemas de transacciones distribuidas requiere algunos conocimientos básicos de sistemas distribuidos como guía teórica.

2.1 teorema de la PAC

El teorema CAP fue propuesto por Eric Brewer, un científico informático de la Universidad de California, en 1998. Dice que a menudo hay tres indicadores en los sistemas distribuidos.

Ellos son:

  • Consistencia
  • Disponibilidad
  • Tolerancia de partición (tolerancia a fallos de partición)

Lo que dijo Eric es que es imposible que un sistema distribuido cumpla estos tres indicadores al mismo tiempo.

Si te pareces a estos tres círculos, ¿no distingues simplemente tres características?

imagen-20230328211612211

Pero ya ves, estos tres círculos no se superpondrán en tres al mismo tiempo, como mucho se superpondrán en dos.

Entonces esta conclusión se llama teorema CAP. Entonces, ¿por qué ocurre tal situación?

Primero debemos comprender qué representan la coherencia, la disponibilidad y la tolerancia de partición antes de poder comprender el significado.

2.1.1.Coherencia

Así que echemos un vistazo al primero de ellos, la coherencia.

Coherencia significa que los datos obtenidos por los usuarios al acceder a cualquier nodo del sistema distribuido deben ser consistentes. Digamos que ahora tengo dos nodos.

imagen-20230328212029032

Hay un dato en el primer nodo llamado datos cuyo valor es v0, y lo mismo ocurre en el segundo nodo. Entonces, ¿qué forman realmente? Maestro-esclavo.

Ahora bien, cuando un usuario acceda a estos dos nodos, ¿el resultado será el mismo sin importar a quién acceda?

imagen-20230328212247822

Pero ahora si modifico los datos del nodo 1 nodo.

imagen-20230328212305352

¿Los datos de los dos nodos son diferentes en este momento?

Entonces, ¿qué haces para lograr la coherencia?

¿Tiene que sincronizar los datos del nodo 01 al nodo 02?

imagen-20230328212322284

Una vez que se completa la sincronización de datos entre los dos, ¿los datos vuelven a ser consistentes? Entonces, como sistema distribuido.

Al realizar una copia de seguridad de los datos, debe completar la sincronización de los datos de manera oportuna para garantizar la coherencia.

2.1.2 Disponibilidad

El segundo concepto es la disponibilidad.

Dice que cuando los usuarios acceden a cualquier nodo en buen estado del clúster, deben obtener una respuesta en lugar de un tiempo de espera o un rechazo.

Por ejemplo, actualmente tengo tres nodos en este clúster.

imagen-20230328212442424

En circunstancias normales, no existe ningún problema para que los usuarios accedan a cualquiera de ellos.

imagen-20230328212504702

Ahora, estos tres nodos no han experimentado ningún tiempo de inactividad. Pero no sé por qué, por ejemplo, para este nodo3, su solicitud será bloqueada o rechazada.

imagen-20230328212521372

Entonces no se podrá acceder a todas las solicitudes que lleguen. En este momento, el nodo3 no estará disponible. Entonces, la disponibilidad se refiere a si se puede acceder a este nodo normalmente.

2.1.3 Tolerancia a fallos de partición

El tercer concepto: tolerancia a fallos de partición.

La partición se refiere al hecho de que algunos nodos en un sistema distribuido pierden conexiones con otros nodos debido a fallas de la red u otras razones, formando particiones independientes.

Por ejemplo, estos tres nodos son el nodo 123.

imagen-20230328213428467

Entonces el usuario podrá acceder a cualquiera de ellos.

Pero debido a una falla de la red, la máquina no se bloqueó y luego el nodo 3 se desconectó del nodo 1 y del nodo 2.

imagen-20230328213509738

El nodo 1 y el nodo 2 pueden acceder entre sí normalmente, pero el nodo 3 no. Por lo tanto, todo el grupo se dividirá en dos áreas en este momento.

Entonces el nodo1 y el nodo2 están en la misma área y el propio nodo3 es una partición.

En este momento, si un usuario escribe nuevos datos en el nodo 02.

imagen-20230328213621216

Entonces el nodo 02 puede sincronizar datos con el nodo 01.

imagen-20230328213631968

Pero entonces ¿hay alguna sincronización en el nodo 3?

Obviamente no, porque no pueden sentirlo, entonces, ¿cómo pueden sincronizarlo?

Entonces los datos de las dos particiones son inconsistentes.

¿Qué significa tolerancia a fallos de partición?

La tolerancia a fallas significa que independientemente de si hay particiones en el clúster, todo el sistema debe continuar brindando servicios al mundo exterior.

2.1.4.Contradicción

Es decir, aunque su lugar esté dividido, ¿los usuarios aún necesitan acceder a él cuando deberían hacerlo?

Pero si voy a visitar el nodo 1 ahora, ¿el resultado que obtendré será el mismo que el resultado de visitar el nodo 3? no lo mismo.

Por lo tanto, se produce una inconsistencia en los datos y no se cumple la coherencia. Entonces si debo cumplir con la consistencia.

¿Qué tengo que hacer? Entonces ¿puedo hacer esto?

Le pedí al nodo 3 que esperara la recuperación de la red del nodo 2 y la sincronización de datos.

antes de la recuperación. Todas las solicitudes para visitarme, las bloquearé aquí, diciendo que mis datos aún no están listos.

¿Es posible?

Si hago esto, ¿podré satisfacer la coherencia de los datos? Pero su nodo3 es obviamente un nodo saludable, pero está atrapado aquí para solicitudes entrantes y las personas no pueden visitarlo.

¿El nodo3 no deja de estar disponible? Por lo que no satisface la usabilidad.

Entonces, ahora encontrará que cuando la red está particionada, ¿no hay forma de satisfacer la disponibilidad y la coherencia al mismo tiempo? Sin embargo, esta partición es inevitable.

¿Por qué dices eso?

Mientras sea un sistema distribuido, ¿sus nodos están necesariamente conectados a través de la red? Mientras esté conectado a través de Internet, ¿hay alguna forma de garantizar que la red esté 100% siempre en buen estado?

Esto es imposible, ¿verdad?

Entonces podemos pensar que todas las particiones del sistema distribuido definitivamente aparecerán, ya que definitivamente aparecerán las particiones.

Y todo su grupo debe proporcionar servicios al mundo exterior, eso significa.

Se debe lograr la tolerancia de partición, entonces, en este momento, ¿debe elegir entre coherencia y disponibilidad? O tienes consistencia o disponibilidad. No hay manera de estar satisfecho al mismo tiempo,

Ésta es una de las razones del teorema CAP.

2.2 Teoría BASE

Ya hemos estudiado el teorema de CAP. Sabemos que en un sistema distribuido, debido a que la partición es inevitable, hay que elegir entre consistencia y disponibilidad. Sin embargo, estas dos características son en realidad muy importantes. Ninguna de ellas quiere dar arriba, entonces, ¿qué debo hacer?

Bien, entonces la teoría BASE puede resolver este problema.

La teoría BASE es una solución al CAP. De hecho, contiene principalmente tres ideas:

  • Básicamente disponible : cuando ocurre una falla en un sistema distribuido, se permite perder la disponibilidad parcial, es decir, se garantiza la disponibilidad del núcleo.
  • **Estado suave:** Dentro de un cierto período de tiempo, se permite que ocurran estados intermedios, como estados temporales inconsistentes.
  • Eventualmente consistente : aunque no se puede garantizar una consistencia sólida, eventualmente se logrará la consistencia de los datos una vez que finalice el estado suave.

De hecho, la teoría BASE es una forma de conciliar y elegir la contradicción entre consistencia y disponibilidad en CAP.

En CAP, si quieres lograr consistencia, tienes que sacrificar la disponibilidad, pero en BASE, si logramos una fuerte consistencia, tienes que sacrificar la disponibilidad, pero no es indisponibilidad, sino un sacrificio o pérdida temporal de disponibilidad parcial. disponible.

2.3 Ideas para resolver transacciones distribuidas

Habiendo dicho tantas ideas, ¿puede resolver nuestro problema de transacciones distribuidas?

De hecho, es posible. Entonces, ¿qué problemas surgen en nuestras transacciones distribuidas?

Las transacciones distribuidas suelen contener n subtransacciones, cada una de las cuales se ejecuta y envía de forma independiente. Como resultado, algunos tuvieron éxito y otros fracasaron. En ese momento, el estado de todos era inconsistente.

Lo que esperamos es que cada subtransacción en esta transacción distribuida tenga el mismo estado final, ya sea que todas tengan éxito o todas fallen.

Entonces, ¿cómo resolvemos esta transacción distribuida basándonos en la teoría de bases?

La primera solución es en realidad el modelo basado en AP.

Modo AP: cada subtransacción se ejecuta y envía por separado, lo que permite resultados inconsistentes y luego se toman medidas correctivas para restaurar los datos y lograr la coherencia final.

Se trata de satisfacer la disponibilidad y sacrificar un cierto grado de coherencia. Por ejemplo, si cada una de nuestras subtransacciones se ejecuta y envía por separado cuando la ejecutemos en el futuro, algunas tendrán éxito y otras fallarán. Entonces, ¿cómo se llama esto?

A esto se le llama inconsistencia de estatus.

En otras palabras, ¿en qué estás?

Estado blando.

Estado temporal inconsistente, ¿vale, por qué? Una vez completada la ejecución, cada una de nuestras subtransacciones puede tomar un respiro.

Mírense unos a otros, ¿lo han conseguido? Oh, lo logré Uh, ¿lo lograste tú? Oye, ¿qué debo hacer si alguien falla en esa comparación?

No se preocupe en este momento. Tomaremos medidas compensatorias para restaurar los datos. Algunos dicen que se han enviado y no se pueden restaurar. Luego podemos hacer la operación inversa. Por ejemplo, ha agregado un nuevo par antes. ¿equivocado?

Luego lo eliminaré a continuación, ¿no habrá terminado?

¿No restaura esto los datos a su estado original? ¿No logra esto una eventual coherencia?

Entonces este modelo es en realidad una idea de AP.

¿Qué pasa al revés? ¿Puedo llegar a un consenso sólido? Oye, eso también es posible.

Modo CP: cada subtransacción se espera entre sí después de la ejecución, se confirma al mismo tiempo y se revierte al mismo tiempo para lograr una gran coherencia. Sin embargo, mientras la transacción está en espera, se encuentra en un estado débilmente disponible.

Anteriormente, cada subtransacción se ejecutaba y enviaba por separado, si terminas de ejecutarlas todas al principio, no puedes retroceder, ¿verdad?

Pero ahora, después de que se haya ejecutado cada una de mis subtransacciones, no las envíe, solo esperen el uno al otro. Mirémonos el uno al otro. Oye, terminé de ejecutarlo. ¿Tú terminaste de ejecutarlo? De esta forma no hay ningún problema hasta que los hayamos ejecutado todos.

Luego nos sometemos al mismo tiempo o alguien falla en el medio, luego retrocedemos al mismo tiempo, entonces, ¿podemos llegar a un consenso sólido? No existe un estado intermedio, ¿verdad?

Pero en este proceso, ¿las distintas subtransacciones tienen que esperarse unas a otras? Esperando la ejecución de cada uno, por lo que durante este proceso, su servicio en realidad se encuentra en un estado débilmente disponible.

Porque bloqueará el recurso y lo hará inaccesible.

¿Crees que hemos implementado una idea para resolver transacciones distribuidas basándonos en la teoría de la base?

Más adelante resolveremos transacciones distribuidas en base a esta idea, pero ya sea CP o AP, aquí hay una cosa en común, es decir, cada subtransacción tendrá que comunicarse entre sí en el futuro para identificar el estado de ejecución de la otra parte.

Entonces, ¿cómo comunicarse entre cada subtransacción?

Por lo tanto, necesita un coordinador para ayudar a cada subtransacción en la transacción distribuida a comunicarse y percibir. El estado de la otra parte o de cada uno, entonces demos un ejemplo, tomemos el pedido que hicimos antes como ejemplo.

imagen-20230329192722139

El usuario realiza un pedido y llama al servicio de pedidos, y luego llama al servicio de contabilidad y al servicio de inventario. ¿Qué pasa con este lugar? Necesitamos tener un coordinador de transacciones y luego cada microservicio mantiene contacto con el coordinador de transacciones.

imagen-20230329192830032

Una vez que llega el negocio, todos lo ejecutan de forma independiente. Si desea una mayor coherencia ahora, es mejor no enviarlo cuando se ejecute el tercer servicio. Realizar servicio de deducción de pedidos, realizar servicio de deducción de inventario y realizar inventario.

Pero después de la ejecución, se descubrió que el inventario falló.

imagen-20230329192952905

¿Cómo lo sabes? ¿Quieren informar al coordinador de los resultados de su ejecución? Luego, el coordinador ve que alguien ha fallado y le notifica que realice la reversión en el futuro.

Entonces, ¿pueden todos ser coherentes? Por lo tanto, el coordinador de esta transacción juega un papel muy clave, durante todo el proceso participamos en las transacciones de cada subsistema en la transacción distribuida, a la que llamamos transacciones de sucursal.

Toda la transacción de la sucursal se denomina transacción global, por lo que el coordinador de transacciones en realidad está aquí para coordinar el estado de cada transacción de la sucursal para que puedan llegar a un acuerdo.

Supongo que te gusta

Origin blog.csdn.net/weixin_53041251/article/details/132419933
Recomendado
Clasificación