¿Por qué puede ser necesaria la replicación semisíncrona?

Hola a todos. Soy Vladislav Rodin. Actualmente enseño en el portal OTUS cursos relacionados con la arquitectura de software y la arquitectura de software de alta carga. Con el inicio de un nuevo ciclo del curso «Arquitecto de altas cargas» decidí escribir un pequeño material autoral que quiero compartir con ustedes.

¿Por qué puede ser necesaria la replicación semisíncrona?

Introducción

Debido a que en un HDD solo se pueden realizar aproximadamente 400-700 operaciones por segundo (lo cual es incomparably con los típicos rps que se requieren en un sistema de alta carga), una base de datos clásica se convierte en un cuello de botella arquitectónico. Por tanto, es necesario prestar atención especial a los patrones de escalado de este almacenamiento.

En este momento existen 2 patrones de escalado de bases de datos: replicación y sharding. El sharding permite escalar las operaciones de escritura y, como consecuencia, reducir el rps de escritura que recae en un servidor de su clúster. La replicación permite hacer lo mismo, pero con las operaciones de lectura. Este último patrón es el que aborda este artículo.

Replicación

Si miramos la replicación desde una perspectiva general, es algo simple: tenías un servidor donde estaban los datos, y luego ese servidor dejó de manejar la carga de lectura de esos datos. Agregas un par de servidores, sincronizas los datos en todos los servidores, y el usuario puede leer desde cualquier servidor de tu clúster.

A pesar de su aparente simplicidad, existen varias formas de clasificar diferentes implementaciones de este esquema:

  • Por roles en el clúster (master-master o master-slave)
  • Por los objetos transmitidos (basado en filas, basado en sentencias o mixto)
  • Por el mecanismo de sincronización de nodos

Hoy abordaremos precisamente el tercer punto.

Cómo ocurre el commit de la transacción

Este tema no se relaciona directamente con la replicación, podría escribirse un artículo por separado, sin embargo, dado que sin entender el mecanismo del commit de transacciones la lectura posterior es inútil, me permitiré recordar las cosas más básicas. El commit de la transacción ocurre en 3 etapas:

  1. Registro de la transacción en el diario de la base de datos.
  2. Aplicación de la transacción en el motor de la base de datos.
  3. Devolución de la confirmación al cliente sobre la aplicación exitosa de la transacción.

En diversas bases de datos, este algoritmo puede tener matices: por ejemplo, en el motor InnoDB de MySQL hay dos registros: uno para replicación (binary log) y otro para mantener ACID (undo/red log), mientras que en PostgreSQL hay un registro que cumple ambas funciones (write ahead log = WAL). Sin embargo, aquí se presenta la concepción general que permite no considerar tales matices.

Replicación sincrónica (sync)

Ahora añadamos lógica al algoritmo de confirmación de transacciones para replicar los cambios recibidos:

  1. Registro de la transacción en el diario de la base de datos.
  2. Aplicación de la transacción en el motor de la base de datos.
  3. Envío de datos a todos los réplicas.
  4. Recepción de confirmación de todas las réplicas sobre la ejecución de la transacción en ellas.
  5. Devolución de la confirmación al cliente sobre la aplicación exitosa de la transacción.

Con este enfoque, enfrentamos una serie de desventajas:

  • el cliente espera la aplicación de cambios en todas las réplicas.
  • con un aumento en el número de nodos en el clúster, disminuimos la probabilidad de que la operación de escritura se realice con éxito.

Si bien el primer punto es más o menos claro, las razones del segundo punto merecen explicación. Si en la replicación sincrónica no recibimos respuesta de al menos un nodo, revertimos la transacción. Así, al aumentar el número de nodos en el clúster, aumentamos la probabilidad de que la operación de escritura falle.

¿Podemos esperar confirmación solo de una parte de los nodos, por ejemplo, del 51% (quorum)? Sí, podemos, sin embargo, en la variante clásica se requiere confirmación de todos los nodos, ya que así aseguramos la plena consistencia de los datos en el clúster, lo que es una gran ventaja de este tipo de replicación.

Replicación asincrónica (async)

Modifiquemos el algoritmo anterior. Vamos a enviar los datos a las réplicas "en algún momento después", y "en algún momento después" los cambios aplicados en las réplicas:

  1. Registro de la transacción en el diario de la base de datos.
  2. Aplicación de la transacción en el motor de la base de datos.
  3. Devolución de la confirmación al cliente sobre la aplicación exitosa de la transacción.
  4. Envío de datos a las réplicas y aplicación de cambios por ellas.

Este enfoque hace que el clúster funcione rápidamente, ya que no mantenemos al cliente esperando hasta que los datos lleguen a las réplicas y se confirmen.

Sin embargo, la condición de enviar datos a las réplicas "en algún momento después" puede llevar a la pérdida de transacciones, y de hecho, a la pérdida de transacciones confirmadas al usuario, ya que si los datos no se replicaron a tiempo, se envió una confirmación al cliente sobre la correcta realización de la operación, y si el nodo que recibió los cambios tiene un fallo de HDD, perdemos la transacción, lo que puede tener consecuencias muy desagradables.

Replicación semisincrónica (semisync)

Finalmente hemos llegado a la replicación semisincrónica. Este tipo de replicación no es muy conocido ni muy común, sin embargo, es de gran interés, ya que puede combinar las ventajas de la replicación tanto sincrónica como asincrónica.

Intentemos combinar los 2 enfoques anteriores. No mantendremos al cliente esperando por mucho tiempo, pero requeriremos que los datos sean replicados:

  1. Registro de la transacción en el diario de la base de datos.
  2. Aplicación de la transacción en el motor de la base de datos.
  3. Envío de datos a las réplicas.
  4. Recepción de confirmación de la réplica sobre la recepción de los cambios (se aplicarán 'en algún momento después').
  5. Devolución de la confirmación al cliente sobre la aplicación exitosa de la transacción.

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

Pero con este enfoque existe el riesgo de lecturas fantasma. Imaginemos el siguiente escenario: en el paso 4 no recibimos confirmación de ninguna réplica. Debemos retroceder esta transacción y no devolver confirmación al cliente. Dado que los datos se aplicaron en el paso 2, entre la finalización del paso 2 y el retroceso de la transacción hay un vacío temporal, durante el cual transacciones paralelas pueden ver esos cambios que no deberían estar en la base de datos.

Replicación semisincrónica sin pérdida

Si reflexionamos un poco, simplemente cambiando el orden de los pasos del algoritmo, podemos solucionar el problema de las lecturas fantasma en este escenario:

  1. Registro de la transacción en el diario de la base de datos.
  2. Envío de datos a la réplica.
  3. Recepción de confirmación de la réplica sobre la recepción de los cambios (se aplicarán 'en algún momento después').
  4. Aplicación de la transacción en el motor de la base de datos.
  5. Devolución de la confirmación al cliente sobre la aplicación exitosa de la transacción.

Ahora solo confirmamos los cambios si han sido replicados.

Salida

Como siempre, no existen soluciones ideales, solo un conjunto de soluciones, cada una con sus ventajas y desventajas, adecuadas para resolver diferentes clases de problemas. Esto es igualmente cierto para la elección del mecanismo de sincronización de datos en una base de datos replicada. El conjunto de ventajas que ofrece la replicación semisincrónica es bastante sólido e interesante como para considerarla digna de atención, a pesar de su baja difusión.

Eso es todo. Nos vemos en curso!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster