Artículos

Cuándo y cómo configurar una réplica de lectura en Django

Una de las tareas más importantes de la ingeniería de software es elegir la arquitectura que mejor resuelva un problema específico. Una elección incorrecta puede producir un sitio que no funcione y afectar a tu negocio. Uno de los problemas más comunes de las arquitecturas es el rendimiento.

Los problemas de rendimiento suelen aparecer en sistemas grandes y son difíciles de diagnosticar porque cuesta reproducirlos; a veces solo ocurren en un entorno específico. Pueden deberse a factores como el hardware, la red o un programa poco eficiente. En esta publicación nos centraremos en los problemas de rendimiento de la base de datos y en cómo abordarlos con réplicas de lectura.

Problemas de rendimiento en bases de datos

La base de datos es una de las principales fuentes de lentitud de un sistema y puede deberse a varios factores. Uno de los más comunes es un diseño de esquema incorrecto. Una base de datos que no se diseñó pensando en las operaciones previstas probablemente tendrá un rendimiento deficiente. Sin embargo, un esquema bien diseñado no garantiza un buen rendimiento; otros factores, como la cantidad de conexiones, pueden afectarlo. Si miles de usuarios usan una única base de datos, recibirá muchas conexiones. Cuando son demasiadas, no tiene suficientes recursos para atenderlas todas en tiempo real y surgen problemas de rendimiento. Por suerte, existe una solución: las réplicas.

Réplicas en bases de datos

La replicación en software y hardware consiste en usar múltiples copias de un recurso para garantizar rendimiento, disponibilidad y tolerancia a fallos. En bases de datos, hablamos de varios servidores que trabajan con los mismos «datos». Hay diversas configuraciones de replicación, por ejemplo:

  • Todas las instancias permiten lectura y escritura

  • Una única instancia principal que permite lectura y escritura, y varias instancias de solo lectura . En ese esquema, la replicación y la sincronización de datos pueden ser síncronas o asíncronas.

En esta publicación hablaremos de la segunda configuración y supondremos que la réplica de lectura se actualiza de forma síncrona.

Configurar réplicas de lectura en una aplicación Django

Para configurar y usar réplicas de lectura en una aplicación Django, debemos ocuparnos de estos puntos:

  • Declarar las instancias de réplica de lectura como bases de datos en la aplicación Django

  • Configurar un enrutador para elegir las réplicas de lectura cuando sea necesario

Declarar las instancias de réplica de lectura como bases de datos

Para permitir su uso, debemos declararlas como bases de datos adicionales en el archivo de configuración de Django. Este paso es sencillo y puede hacerse siguiendo la documentación oficial sobre múltiples bases de datos.

La idea es definir la instancia principal, el servidor de lectura y escritura, como base de datos default, y declarar las réplicas como bases de datos adicionales. Por ejemplo, si configuramos dos réplicas, el archivo debería verse así:

DATABASES = {
    'default': {
        'NAME': 'master',
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'host_to_the_master_instance'
        'USER': 'master_postgres_user',
        'PASSWORD': 'master_password'
    },
    'replica_1': {
        'NAME': 'replica_1',
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'host_to_the_replica_1_instance'
        'USER': 'replica_1_postgres_user',
        'PASSWORD': 'replica_1_password'
    },
    'replica_2': {
        'NAME': 'replica_2',
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'host_to_the_replica_2_instance'
        'USER': 'replica_2_postgres_user',
        'PASSWORD': 'replica_2_password'
    },
}

Después de esta configuración, las dos réplicas deberían estar disponibles en la aplicación Django. Para comprobarlo, podemos usar el método using en un queryset. Por ejemplo, podemos probar que accedemos al modelo User desde todas las bases de datos, tanto la principal como las réplicas.

# The users are retrieved from the master instance
User.objects.all()

# The users are retrieved from the replica 1 instance
User.objects.using('replica_1').all()

# The users are retrieved from the replica 2 instance
User.objects.using('replica_2').all()

También podemos indicar qué base de datos usar al guardar un objeto. Si intentamos guardarlo en una réplica, obtendremos un error porque la instancia solo permite operaciones de lectura.

# To save the instance in the default database. 
# The operation is OK
u = User(email='user@user.com')
u.save()

# Try to save the instance in the replica instance. 
# The operation will throw an error
u = User(email='user@user.com')
u.save(using='replica_1')

Configurar un enrutador para elegir las réplicas de lectura cuando sea necesario

Con el paso anterior, la aplicación Django tiene las réplicas disponibles y podemos acceder a ellas manualmente cuando lo necesitemos. Sin embargo, esta solución no escala porque es responsabilidad del desarrollador detectar las operaciones de solo lectura y elegir la réplica. Además, si en el futuro creamos más réplicas, tendremos que revisar todo el código y elegir manualmente cuál usar en cada operación.

La alternativa es un enrutador de bases de datos de Django. Podemos crear uno personalizado que use la instancia predeterminada para escritura y elija una réplica aleatoria para lectura. Un ejemplo es CustomRouter:

import random

class CustomRouter:
    def db_for_read(self, model, **hints):
        """
        Return one of the replicas
        """
        return random.choice(['replica_1', 'replica_2'])

    def db_for_write(self, model, **hints):
        # Always return the default database
        return 'default'

    def allow_relation(self, obj1, obj2, **hints):
        return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return True

Luego debemos indicar en la configuración de Django que queremos usar este enrutador en lugar del predeterminado.

DATABASE_ROUTERS = ['path.to.CustomRouter’]

Después de estos cambios, todas las operaciones de lectura usarán las réplicas y las de escritura usarán la instancia principal.


En resumen, habilitar réplicas de lectura en Django es bastante sencillo y puede mejorar el rendimiento. Como mostramos, los únicos cambios necesarios son de configuración; el resto del código se verá igual. Esperamos que esta publicación te ayude a entender cómo usar réplicas en una aplicación Django y conseguir un mejor rendimiento.

“Cuándo y cómo configurar una réplica de lectura en Django” de Pablo Grill está bajo la licencia CC BY SA. Los ejemplos de código fuente están bajo la licencia MIT.

Foto de sophilabs.

Clasificado en Investigación y aprendizaje.

Lecturas relacionadas