Artigos

Quando e como configurar uma réplica de leitura no Django

Uma das tarefas mais importantes da engenharia de software é escolher a arquitetura que melhor resolve um problema específico. Uma escolha errada pode resultar em um site que não funciona e afetar seu negócio. Um dos problemas mais comuns das arquiteturas é o desempenho.

Problemas de desempenho costumam aparecer em sistemas grandes e são difíceis de diagnosticar porque é difícil reproduzi-los; às vezes, só ocorrem em um ambiente específico. Podem ser causados por fatores como hardware, rede ou um programa pouco eficiente. Nesta publicação, vamos focar nos problemas de desempenho do banco de dados e em como resolvê-los com réplicas de leitura.

Problemas de desempenho em bancos de dados

O banco de dados é uma das principais fontes de lentidão de um sistema, causada por vários fatores. Um dos mais comuns é um projeto de esquema incorreto. Um banco que não foi projetado considerando as operações previstas provavelmente terá desempenho ruim. Porém, um esquema bem projetado não garante bom desempenho; outros fatores, como o número de conexões, podem afetá-lo. Se milhares de usuários usam um único banco de dados, ele receberá muitas conexões. Quando são numerosas demais, não há recursos suficientes para atender a todas em tempo real, causando problemas de desempenho. Felizmente, há uma solução: as réplicas.

Réplicas em bancos de dados

A replicação em software e hardware consiste em usar várias cópias de um recurso para garantir desempenho, disponibilidade e tolerância a falhas. Em bancos de dados, falamos de vários servidores trabalhando com os mesmos “dados”. Existem diversas configurações de replicação, por exemplo:

  • Todas as instâncias permitem leitura e escrita

  • Uma única instância principal que permite leitura e escrita, e várias instâncias somente de leitura . Nesse esquema, a replicação e a sincronização dos dados podem ser síncronas ou assíncronas.

Nesta publicação, vamos falar da segunda configuração, supondo que a réplica de leitura é atualizada de forma síncrona.

Configurar réplicas de leitura em uma aplicação Django

Para configurar e usar réplicas de leitura em uma aplicação Django, precisamos cuidar destes pontos:

  • Declarar as instâncias de réplica de leitura como bancos de dados na aplicação Django

  • Configurar um roteador para escolher as réplicas de leitura quando necessário

Declarar as instâncias de réplica de leitura como bancos de dados

Para permitir seu uso, precisamos declará-las como bancos adicionais no arquivo de configurações do Django. Esse passo é fácil e pode ser feito seguindo a documentação oficial sobre múltiplos bancos de dados.

A ideia é definir a instância principal, o servidor de leitura e escrita, como banco default, e declarar as réplicas como bancos adicionais. Por exemplo, se configuramos duas réplicas, o arquivo deve ficar assim:

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'
    },
}

Depois dessa configuração, as duas réplicas devem estar disponíveis na aplicação Django. Para verificar, podemos usar o método using em um queryset. Por exemplo, podemos testar o acesso ao modelo User a partir de todos os bancos, tanto o principal quanto as 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()

Também podemos especificar qual banco usar ao salvar um objeto. Se tentarmos salvá-lo em uma réplica, receberemos um erro porque a instância só permite operações de leitura.

# 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 um roteador para escolher as réplicas de leitura quando necessário

Com o passo anterior, a aplicação Django tem as réplicas disponíveis e podemos acessá-las manualmente quando necessário. Porém, essa solução não escala, pois é responsabilidade do desenvolvedor identificar operações somente de leitura e escolher a réplica. Além disso, se criarmos mais réplicas no futuro, precisaremos revisar todo o código e escolher manualmente qual usar em cada operação.

A alternativa é um roteador de bancos de dados do Django. Podemos criar um roteador personalizado que use a instância padrão para escrita e escolha uma réplica aleatória para leitura. Um exemplo é 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

Depois, precisamos indicar nas configurações do Django que queremos usar esse roteador em vez do padrão.

DATABASE_ROUTERS = ['path.to.CustomRouter’]

Após essas mudanças, todas as operações de leitura usarão as réplicas, enquanto as de escrita usarão a instância principal.


Em resumo, habilitar réplicas de leitura no Django é bastante simples e pode melhorar o desempenho. Como mostramos, as únicas mudanças necessárias são de configuração; o restante do código continuará igual. Esperamos que esta publicação ajude você a entender como usar réplicas em uma aplicação Django e conseguir um desempenho melhor.

“Quando e como configurar uma réplica de leitura no Django” por Pablo Grill está sob a licença CC BY SA. Os exemplos de código-fonte estão sob a licença MIT.

Foto de sophilabs.

Classificado em Pesquisa e aprendizado.

Leituras relacionadas