Background Image
DESARROLLO DE SOFTWARE

Retos y estrategias en la implantación de la conmutación por error inteligente en sistemas de bases de datos

Sergiy Voshchykov

Desarrollador Senior

Aaron Congo

Desarrollador de software sénior

August 23, 2022 | 9 Minuto(s) de lectura

Las bases de datos son una parte esencial de cualquier sistema moderno y, a medida que ha aumentado su popularidad, también lo han hecho los estándares de rendimiento, disponibilidad y persistencia. Aunque las bases de datos modernas ofrecen mejoras significativas en estas áreas, los fallos eventuales de las bases de datos son inevitables.

Esta entrada del blog explora diferentes estrategias que se pueden utilizar para corregir los fallos de las bases de datos lo más rápido posible.

Image - Intelligent Failover -2

Hoy en día se utilizan muchos tipos diferentes de bases de datos. Entre las más comunes se encuentran:

  • Bases de datos relacionales

  • Bases de datos NoSQL

  • Bases de datos gráficas

  • Almacenes de claves/valores

  • Bases de datos especializadas para:

    • Coordenadas geológicas

    • Líneas temporales/Series cronológicas

En las últimas décadas, las aplicaciones se han hecho más grandes y pesadas. Funcionan con cantidades ingentes de datos, y las bases de datos han tenido que adaptarse a las demandas que las acompañan. Como resultado, las bases de datos monoservidor han sido ampliamente sustituidas por bases de datos multiservidor alojadas en la nube. Estos sistemas suelen denominarse clústeres de bases de datos. Las bases de datos en la nube ofrecen una gran ventaja por su capacidad para escalar rápidamente, pero también añaden cierta complejidad y retos adicionales desde la perspectiva de la aplicación cliente. Por ejemplo

  • Accesibilidad a la red: las instancias de la base de datos pueden bloquearse o volverse inaccesibles en cualquier momento, y las aplicaciones cliente necesitan gestionar con elegancia estos fallos espontáneos.

  • Establecimiento de nuevas conexiones: las bases de datos multiinstancia introducen incertidumbre sobre a qué instancia debe conectarse la aplicación. ¿Qué instancias están muy cargadas? ¿Se han añadido nuevas instancias? ¿Están todas las instancias disponibles? Las aplicaciones cliente deben conocer la topología de la base de datos y hacer un seguimiento de su estado a lo largo del tiempo.

Image - Intelligent Failover #1

Para lograr la coherencia de los datos entre instancias, los clústeres de bases de datos suelen compartir datos entre cada instancia. También proporcionan tolerancia a fallos y escalabilidad realizando procesos complejos que se abstraen de la aplicación. Estos procesos incluyen

  • Replicación de datos entre instancias

  • Resolución de conflictos de datos

  • Copias de seguridad y recuperación de datos

Cuando se utilizan clústeres de bases de datos, las aplicaciones cliente deben tener cuidado a la hora de diferenciar entre la lectura de datos y la escritura de datos. Las topologías de clústeres de bases de datos pueden adoptar diferentes formas, pero en general suelen contener instancias de dos tipos: escritores y lectores (también denominados réplicas). Como es de esperar, los escritores pueden modificar los datos, mientras que los lectores sólo pueden recuperarlos. Las instancias lectoras proporcionan una forma de equilibrar la carga de las peticiones de datos y mejorar así el rendimiento. En este caso, la aplicación cliente debe distinguir entre sentencias de lectura y escritura, y dirigirlas a las conexiones de base de datos apropiadas según el rol de la instancia conectada (lector o escritor).

Topologías de clústeres de bases de datos

Existen muchas implementaciones diferentes de topologías de clústeres de bases de datos. Cada implementación tiene sus pros y sus contras, y los arquitectos de sistemas pueden utilizar la que mejor se adapte a los requisitos de su sistema. A continuación se muestran algunas implementaciones comunes.

Image - Intelligent Failover #3

¿Qué ocurre cuando muere una instancia?

¿Qué ocurre realmente cuando muere una instancia de base de datos? Utilicemos como ejemplo una de las implementaciones de topología de clúster más comunes: un único escritor con múltiples réplicas de lectura. El siguiente diagrama muestra un clúster con cinco instancias. En este ejemplo, la primera instancia de base de datos (S1) experimenta un fallo. ¿Cómo debe responder la aplicación cliente?

Image - Intelligence Failover #4

La situación más sencilla sería que S1 fuera una instancia lectora. Dado que la conexión era a una instancia lectora, sabemos que la conexión sólo se estaba utilizando para sentencias de sólo lectura. Cuando la aplicación cliente detecta un problema con la conexión, puede reemplazarla con una nueva conexión a cualquier otra instancia disponible. Dado que la conexión estaba operando en modo de sólo lectura, sabemos que sólo se estaban ejecutando sentencias de sólo lectura contra la conexión, y cambiar de un lector a otro es seguro.

En el otro escenario, el que se muestra en el diagrama anterior, la instancia S1 era una instancia escritora. En este caso, en el lado del servidor, el clúster de base de datos necesita elegir una nueva instancia de escritor y reconfigurar adecuadamente el clúster. La instancia de escritor recién elegida asume las responsabilidades de escritor y se convierte en la principal fuente de cambios de datos. Este proceso se denomina failover. Existe un tiempo de inactividad inevitable asociado a la conmutación por error del clúster, y las aplicaciones cliente deben gestionarlo adecuadamente. En este escenario, la aplicación cliente necesitará alguna forma de reconectarse al nuevo nodo escritor una vez que esté disponible. El mecanismo para realizar esta acción depende de la implementación de cluster que se utilice. Veamos un ejemplo de implementación de clúster y cómo resuelve este problema.

Image - Intelligence Failover #5

La implementación de clúster anterior tiene algunas características únicas:

  • Replicación: normalmente, la replicación se realiza a través de la comunicación directa entre las instancias escritora y lectora. Un escritor envía un registro de cambios a las instancias lectoras, y cada lector aplica estos cambios a su propia copia de los archivos de la base de datos. Esta implementación, en cambio, toma un atajo y aplica los cambios de datos directamente a los archivos de datos de los lectores. Esta operación es segura porque las conexiones del cliente no pueden cambiar los datos en las instancias lectoras, y es más rápida que el enfoque típico. El lector simplemente sirve los datos actualizados a la aplicación cliente.

  • Zonas de disponibilidad: las instancias de la base de datos se despliegan en hardware físico cableado a diferentes líneas eléctricas. El centro de datos está organizado de tal manera que al menos 2 zonas de disponibilidad sobreviven a cualquier corte de energía. Este enfoque ayuda a minimizar los tiempos de inactividad del clúster. El mismo enfoque es aplicable a cualquier actualización/mejora de software: se puede aplicar una actualización a una instancia de una zona de disponibilidad concreta mientras las instancias de las demás zonas de disponibilidad siguen activas y utilizables.

Para proporcionar acceso a las distintas instancias del clúster, este sistema de base de datos expone puntos finales especiales "clúster" y "lector". Los puntos finales son nombres de dominio y nunca cambian, pero las direcciones IP subyacentes a las que apuntan pueden cambiar. El punto final de clúster proporciona una función útil: siempre apunta a la instancia de escritor actual. Si la instancia de escritor cambia durante un proceso de failover, entonces el cluster endpoint dirigirá las conexiones a esta nueva instancia de escritor. Esto resuelve el problema mencionado anteriormente: en caso de que falle una instancia de escritor, la aplicación cliente puede utilizar el punto final del clúster para conectarse a la nueva instancia de escritor.

El punto final de lector funciona de forma similar, pero proporciona a la aplicación cliente una única conexión de lector (aleatoria) de la lista de instancias de lector disponibles. Cada vez que el cliente se conecta utilizando el punto final de lector, la conexión resultante podría ser a cualquiera de los lectores disponibles. Esto simplifica el acceso a las instancias lectoras y ayuda a equilibrar la carga entre ellas.

Image - Intelligence Failover #6

Con este sistema de puntos finales, la aplicación cliente no necesita saber nada sobre las instancias individuales de la base de datos (como las direcciones IP o los nombres DNS asociados). Sólo necesita un cluster o un endpoint lector. Llegados a este punto, se podría pensar que la complejidad de seleccionar a qué instancia conectarse está resuelta. Sin embargo, hay algunos aspectos más que tendremos que considerar.

El uso de endpoints nos obliga a aceptar que los nombres de dominio deben resolverse en direcciones IP asociadas cada vez que la aplicación cliente los utilice. La resolución DNS se realiza haciendo una llamada al servidor DNS más cercano, que utiliza datos almacenados en caché. En el caso de la conmutación por error del escritor, cuando la instancia del escritor ha cambiado, no hay garantía de que el registro DNS actualizado se propague inmediatamente. En otras palabras, puede haber un intervalo de tiempo en el que se haya elegido un nuevo escritor, pero la aplicación cliente aún no pueda acceder a él a través del punto final del clúster, que todavía se está resolviendo a la nueva dirección IP. En este escenario, la aplicación cliente podría potencialmente ser dirigida a una dirección IP obsoleta en lugar de a la instancia de escritor que solicitó.

Pero, ¿y si hubiera un componente inteligente entre la aplicación cliente y el clúster de base de datos que lo supiera todo sobre la topología y el estado del clúster? Algo así:

Image - Intelligence Failover #7

Si este fuera el caso, la aplicación cliente podría simplemente conectarse al router utilizando un único punto de acceso, sin requerir ninguna configuración compleja. El router conocería la topología actual del clúster, qué instancia es la escritora y qué instancias están caídas en ese momento. Al conocer la topología y el estado del clúster, podría distribuir la carga uniformemente entre las instancias lectoras. Además, podría ser utilizado por aplicaciones completamente diferentes para acceder al mismo clúster o incluso a otros clústeres de bases de datos. Esto podría hacerse sin necesidad de que la aplicación cliente volviera a implementar la misma lógica para cada emparejamiento aplicación/cluster.

Veamos esta idea desde otro ángulo. El diagrama siguiente presenta un modelo de controlador de base de datos. En este modelo, la aplicación cliente utiliza una interfaz común para acceder a una base de datos - por ejemplo, la interfaz JDBC para una aplicación Java. La interfaz JDBC unifica el acceso a diferentes bases de datos ocultando los detalles específicos de la base de datos dentro de la lógica del controlador.

Image - Intelligence Failover #9

¿Qué pasaría si se pudiera utilizar un controlador para la implementación del enrutador del que hemos hablado antes? ¿Un controlador que, además de proporcionar conectividad general, conozca la topología actual del clúster? Llamémoslo controlador inteligente. El controlador inteligente podría utilizar su conocimiento actualizado de la topología para proporcionar una conexión más rápida al nuevo escritor en caso de conmutación por error. Veamos cómo conseguirlo.

Image - Intelligence Failover #10

Los controladores sirven una conexión lógica a una aplicación y la asocian internamente con una conexión física a una instancia de base de datos. Con este modelo, podríamos cambiar fácilmente la conexión física subyacente de una instancia a otra, sin requerir la acción de la aplicación cliente. Para habilitar la funcionalidad de conmutación por error rápida que deseamos, nuestro controlador inteligente necesita realizar un seguimiento de la topología del clúster utilizando la conexión actual a la base de datos. Cuando se detecta un fallo en la base de datos, el controlador conoce la topología actual, pero no qué instancia es el nuevo escritor. Para resolver este problema, el controlador podría utilizar la lista de lectores disponibles para establecer una nueva conexión con la base de datos. Utilizando esta conexión, obtendría y analizaría continuamente la topología del cluster. En cuanto un nuevo escritor esté disponible, el controlador sustituirá la conexión física con el antiguo escritor por una conexión física con el nuevo. Todo este proceso podría realizarse incluso sin interrumpir la aplicación cliente.

Sin embargo, hay algo más que debemos tener en cuenta para abstraer adecuadamente este proceso de la aplicación: cada conexión a una instancia de base de datos está asociada a un contexto de sesión específico.

Image - Intelligence Failover #11

Un contexto de sesión es un grupo de configuraciones, variables temporales y objetos de base de datos temporales que pueden crearse directa o indirectamente a lo largo de la vida de una conexión. Una tabla o función temporal, el último ID insertado de la última sentencia INSERT/UPDATE, los ajustes de zona horaria, los ajustes de configuración regional... todos ellos son buenos ejemplos de contexto de sesión.

Cuando el controlador cambia la conexión física subyacente durante la conmutación por error, también pierde indirectamente la información relacionada con el contexto de sesión. Si el flujo de sentencias SQL depende de este contexto, la aplicación cliente puede experimentar errores o pérdidas de datos. Obviamente, esto no es lo ideal. ¿Cómo podemos solucionar este problema? Por desgracia, la solución es bastante compleja. Una opción sería analizar las sentencias SQL enviadas a través del controlador y rastrear el estado de la sesión en consecuencia. Sin embargo, la gran variedad de formas en que se puede establecer el estado de la sesión hace que esta tarea sea bastante complicada, y el alcance de cómo implementar esta funcionalidad está fuera del alcance de este artículo. Otra opción sería notificar a la aplicación de un cambio de conexión y dejar la responsabilidad de gestionar el estado de la sesión a la propia aplicación. En cierto modo, este enfoque tiene sentido, ya que la aplicación tiene la perspectiva más completa de lo que se intenta conseguir con las ejecuciones de sentencias SQL. Sin embargo, en este escenario no podemos abstraer completamente el proceso de la aplicación - una excepción tendrá que ser lanzada alertando a la aplicación del cambio de conexión para que pueda reconfigurar el estado de la sesión según sea necesario.

Ahí lo tienes. En este post, hemos echado un vistazo a los clusters de bases de datos y cómo funcionan, los fallos de bases de datos y cómo manejarlos, y algunos de los retos y estrategias utilizados para minimizar los tiempos de inactividad cuando se producen fallos. Esperamos que hayas adquirido nuevas perspectivas sobre el mundo de los clústeres de bases de datos y su funcionamiento. Si has disfrutado leyendo este post, asegúrate de volver en el futuro para obtener más información sobre temas relacionados; ¡esperamos volver a verte pronto!

Desarrollo de software
Platform Engineering
Datos

Reflexiones más recientes

Explore las entradas de nuestro blog e inspírese con los líderes de opinión de todas nuestras empresas.
Blog Image - Unveiling the Future of AI at Google Cloud Next 24 -1
IA/ML

Unveiling the Future of AI at Google Cloud Next ‘24

Get firsthand insights from Improving into the innovation brewing around artificial intelligence and cloud computing at Google Cloud Next '24.