¿Por qué podría necesitar una replicación semisincrónica?

Hola a todos. Vladislav Rodin está en contacto. Actualmente imparto cursos de Arquitectura de Software y Arquitectura de Software de Alto Estrés en OTUS. En previsión del inicio de un nuevo curso "Arquitecto de alta carga" Decidí escribir un breve fragmento de material original que quiero compartir con ustedes.

¿Por qué podría necesitar una replicación semisincrónica?

introducción

Debido al hecho de que el disco duro solo puede realizar entre 400 y 700 operaciones por segundo (lo que es incomparable con los rps típicos en un sistema de alta carga), la base de datos en disco clásica es el cuello de botella de la arquitectura. Por tanto, es necesario prestar especial atención a los patrones de escala de este almacenamiento.

Actualmente, existen dos patrones de escalado de bases de datos: replicación y fragmentación. La fragmentación le permite escalar la operación de escritura y, como resultado, reducir los rps por escritura por servidor en su clúster. La replicación le permite hacer lo mismo, pero con operaciones de lectura. Es este patrón al que está dedicado este artículo.

Replicación

Si miras la replicación a un nivel muy alto, es algo simple: tenías un servidor, había datos en él y luego este servidor ya no podía soportar la carga de leer estos datos. Agrega un par de servidores más, sincroniza datos en todos los servidores y el usuario puede leer desde cualquier servidor de su clúster.

A pesar de su aparente simplicidad, existen varias opciones para clasificar las distintas implementaciones de este esquema:

  • Por roles en el cluster (maestro-maestro o maestro-esclavo)
  • Por objetos enviados (basados ​​en filas, basados ​​en declaraciones o mixtos)
  • Según el mecanismo de sincronización de nodos.

Hoy nos ocuparemos del punto 3.

¿Cómo se produce una confirmación de transacción?

Este tema no está directamente relacionado con la replicación, se puede escribir un artículo separado sobre él, pero dado que leer más es inútil sin comprender el mecanismo de confirmación de transacciones, permítame recordarle las cosas más básicas. Una confirmación de transacción ocurre en 3 etapas:

  1. Registrar una transacción en el registro de la base de datos.
  2. Usando una transacción en un motor de base de datos.
  3. Devolver la confirmación al cliente de que la transacción se aplicó exitosamente.

En diferentes bases de datos, este algoritmo puede tener matices: por ejemplo, en el motor InnoDB de la base de datos MySQL hay 2 registros: uno para replicación (registro binario) y otro para mantener ACID (deshacer/rehacer registro), mientras que en PostgreSQL hay un registro que realiza ambas funciones (registro de escritura anticipada = WAL). Pero lo que se presenta arriba es precisamente el concepto general, que permite no tener en cuenta tales matices.

Replicación sincrónica (sincronizada)

Agreguemos lógica para replicar los cambios recibidos al algoritmo de confirmación de transacciones:

  1. Registrar una transacción en el registro de la base de datos.
  2. Usando una transacción en un motor de base de datos.
  3. Envío de datos a todas las réplicas.
  4. Recibir confirmación de todas las réplicas de que se ha completado una transacción en ellas.
  5. Devolver la confirmación al cliente de que la transacción se aplicó exitosamente.

Con este enfoque obtenemos una serie de desventajas:

  • el cliente espera a que los cambios se apliquen a todas las réplicas.
  • A medida que aumenta el número de nodos en el clúster, disminuimos la probabilidad de que la operación de escritura sea exitosa.

Si todo está más o menos claro con el primer punto, entonces vale la pena explicar las razones del segundo punto. Si durante la replicación sincrónica no recibimos una respuesta de al menos un nodo, revertimos la transacción. Por lo tanto, al aumentar la cantidad de nodos en el clúster, aumenta la probabilidad de que falle una operación de escritura.

¿Podemos esperar la confirmación solo de un cierto porcentaje de nodos, por ejemplo, del 51% (quórum)? Sí, podemos, pero en la versión clásica se requiere la confirmación de todos los nodos, porque así es como podemos garantizar una coherencia total de los datos en el clúster, lo cual es una ventaja indudable de este tipo de replicación.

Replicación asincrónica (asincrónica)

Modifiquemos el algoritmo anterior. Enviaremos datos a las réplicas "en algún momento más tarde" y "en algún momento más tarde" los cambios se aplicarán a las réplicas:

  1. Registrar una transacción en el registro de la base de datos.
  2. Usando una transacción en un motor de base de datos.
  3. Devolver la confirmación al cliente de que la transacción se aplicó exitosamente.
  4. Enviar datos a réplicas y aplicarles cambios.

Este enfoque conduce al hecho de que el clúster funciona rápidamente, porque no hacemos que el cliente espere a que los datos lleguen a las réplicas e incluso se confirmen.

Pero la condición de volcar datos en réplicas "en algún momento más tarde" puede provocar la pérdida de una transacción y la pérdida de una transacción confirmada por el usuario, porque si los datos no tuvieron tiempo de replicarse, se enviará una confirmación al cliente. Se envió información sobre el éxito de la operación y el nodo al que llegaron los cambios falló en el disco duro, perdemos la transacción, lo que puede tener consecuencias muy desagradables.

Replicación semisincronizada

Finalmente llegamos a la replicación semisincrónica. Este tipo de replicación no es muy conocido ni muy común, pero es de considerable interés porque puede combinar las ventajas de la replicación sincrónica y asincrónica.

Intentemos combinar los 2 enfoques anteriores. No mantendremos al cliente por mucho tiempo, pero exigiremos que los datos se repliquen:

  1. Registrar una transacción en el registro de la base de datos.
  2. Usando una transacción en un motor de base de datos.
  3. Envío de datos a réplicas.
  4. Recibir confirmación de la réplica de que se han recibido los cambios (se aplicarán “en algún momento más adelante”).
  5. Devolver la confirmación al cliente de que la transacción se aplicó exitosamente.

Tenga en cuenta que con este algoritmo, la pérdida de transacciones ocurre solo si fallan tanto el nodo que recibe los cambios como el nodo de réplica. La probabilidad de que se produzca tal fallo se considera baja y estos riesgos se aceptan.

Pero con este enfoque existe un posible riesgo de lecturas fantasma. Imaginemos el siguiente escenario: en el paso 4, no recibimos confirmación de ninguna réplica. Debemos revertir esta transacción y no devolver una confirmación al cliente. Dado que los datos se aplicaron en el paso 2, hay un lapso de tiempo entre el final del paso 2 y la reversión de la transacción, durante el cual las transacciones paralelas pueden ver cambios que no deberían estar en la base de datos.

Replicación semisincronizada sin pérdidas

Si piensa un poco, puede simplemente revertir los pasos del algoritmo y solucionar el problema de las lecturas fantasmas en este escenario:

  1. Registrar una transacción en el registro de la base de datos.
  2. Envío de datos de réplica.
  3. Recibir confirmación de la réplica de que se han recibido los cambios (se aplicarán “en algún momento más adelante”).
  4. Usando una transacción en un motor de base de datos.
  5. Devolver la confirmación al cliente de que la transacción se aplicó exitosamente.

Ahora confirmamos cambios sólo si se han replicado.

conclusión

Como siempre, no existen soluciones ideales; existe un conjunto de soluciones, cada una de las cuales tiene sus propias ventajas y desventajas y es adecuada para resolver diferentes clases de problemas. Esto es absolutamente cierto a la hora de elegir un mecanismo para sincronizar datos en una base de datos replicada. El conjunto de ventajas que tiene la replicación semisincrónica es lo suficientemente sólido e interesante como para considerarlo digno de atención, a pesar de su baja prevalencia.

Eso es todo. Te veo en por supuesto!

Fuente: habr.com

Añadir un comentario