Avançar para o conteúdo principal
BlogBases de dadosDjango e SQL: O seu Duo Dinâmico para Bases de Dados em Escala

Django e SQL: O seu Duo Dinâmico para Bases de Dados em Escala

ScaleDatabaseswithDjangoandSQL-BlogHeader

Dimensionar e optimizar as bases de dados para satisfazer as necessidades das suas aplicações pode ser um desafio significativo. Se ainda não leu o meu recente blogue sobre como Django pode fazer o trabalho pesado para Python e aplicações de bases de dados SQL, recomendo vivamente que o consulte. Mas a versão TL;DR é que SQL está optimizada para bases de dados SQL, Python não está, e Django é um grande intermediário para o ajudar a construir aplicações mais eficazes, com menos fricção, complexidade, e código quando se utilizam estas duas linguagens em conjunto.

Assim, embora Django faça o trabalho pesado de criar a aplicação de base de dados, continua a ser responsável pela gestão e monitorização diária das suas bases de dados. Algumas destas tarefas de gestão podem ser adiadas para o seu fornecedor de nuvens, utilizando serviços como Linode Managed Databases, mas poderá descobrir novos bloqueios à medida que escalar, como por exemplo:

  • Migrações de bases de dados. Conversão de uma base de dados existente para um novo estado desejado, com alterações controladas ao esquema da base de dados.
  • Implantação de bases de dados múltiplas. Para optimizar o desempenho, os programadores podem conceber as suas aplicações para utilizar bases de dados separadas para funções segmentadas. Por exemplo, uma base de dados primária de leitura/escrita e uma base de dados de réplicas de leitura para consultas comuns.

Se uma das suas bases de dados utiliza SQL, pode utilizar Django para reduzir o atrito e tornar a sua vida muito mais fácil ao mesmo tempo que lida com uma quantidade significativa de dados.

Esta introdução a dois conceitos-chave de gestão de bases de dados, em pares com as instruções passo-a-passo para a construção de uma aplicação Django pronta para a produção, encontrada no ebook Understanding Databases e na minha nova série de vídeos educativos. Qualquer um dos percursos de aprendizagem ajudá-lo-á a conseguir que Django faça o levantamento pesado SQL para si.

Migrações de bases de dados
Quando se começa, obter os tipos de dados certos para qualquer coluna pode ser um pouco complicado, especialmente porque as suas necessidades de dados irão inevitavelmente mudar com o tempo. E se quisesse que o seu campo de título tivesse apenas 80 caracteres? E se precisar de adicionar um campo de carimbo temporal para que possa acompanhar exactamente quando os itens foram adicionados à base de dados?

Mudar uma mesa depois de ter sido criada pode tornar-se bastante confuso por algumas razões:

  • O que se faz com valores pré-existentes?
  • E se faltarem linhas preexistentes para novas colunas/campos?
  • E se remover uma coluna/campo? O que acontece com os dados?
  • E se acrescentar uma relação que não existia antes (ou seja, chaves estrangeiras)?

Felizmente para os desenvolvedores Django, temos algo chamado makemigrations e migrate.

Vejamos como funciona em acção.

Eis o nosso exemplo modelo de dados Django:

class BlogArticle(models.Model):
    user = models.ForeignKey(User, default=1, on_delete=models.SET_DEFAULT)
    title = models.CharField(max_length=120)
    slug = models.SlugField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    publish_timestamp = models.DateTimeField(
        auto_now_add=False,
        auto_now=False,
        blank=True,
        null=True,
    )

Vamos acrescentar o campo:

updated_by = models.ForeignKey(
        User, related_name="editor", null=True, blank=True, on_delete=models.SET_NULL
)

Este campo permitir-nos-á acompanhar o último utilizador a fazer uma alteração ao nosso modelo. 

Vamos actualizar o nosso modelo:

class BlogArticle(models.Model):
    user = models.ForeignKey(User, default=1, on_delete=models.SET_DEFAULT)
    title = models.CharField(max_length=120)
    slug = models.SlugField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    publish_timestamp = models.DateTimeField(
        auto_now_add=False,
        auto_now=False,
        blank=True,
        null=True,
    )
    # our new field
    updated_by = models.ForeignKey(
        User, related_name="editor", null=True, blank=True, on_delete=models.SET_NULL
    )

Agora, depois de guardarmos este ficheiro, este BlogArticle a classe é declarada em (models.py), como é que informamos a nossa base de dados de que esta mudança ocorreu?

Há duas maneiras:

  1. python manage.py makemigrations
  2. python manage.py migrate

Vamos discutir o que fazem estes dois comandos:

python manage.py makemigrations

python manage.py makemigrations procura mudanças em todos models.py ficheiros em todo o seu projecto Django e procura alterações. Se forem encontradas alterações, será criado um novo ficheiro python com o alterações propostas que a nossa base de dados SQL necessidades a fazer. As alterações propostas são algo parecidas:

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('articles', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='article',
            name='updated_by',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='editor', to=settings.AUTH_USER_MODEL),
        ),
    ]

Este, é claro, é apenas mais um ficheiro Python . Este ficheiro permite-nos (aos criadores) saber o que deve acontecer na nossa base de dados. Está escrito em Python e não em SQL para manter a coesão e para tirar partido das características integradas no Django ORM.

Mas porque é que isto é um ficheiro para o que deve acontecer? Bem, há algumas razões para isto:

  • Se precisarmos de rever o que deve acontecer antes de o fazer, podemos apanhá-lo aqui.
  • Isto makemigrations comando não verificar com a base de dados para ver se esta alteração pode mesmo acontecer.
  • A base de dados pode já ter sido alterada para se adequar a estes requisitos (dependendo de uma série de factores relacionados com quem/quem está a gerir a base de dados).
  • Se precisarmos de realizar testes antes de alterar uma base de dados de produção, neste momento seria uma altura incrível para o fazer.

Assumindo que esta mudança é válida (tanto quanto podemos dizer), podemos comprometer as mudanças:

python manage.py migrar

python manage.py migrate tentará alterar a nossa base de dados por nós - todos os campos, colunas, tabelas, chaves estrangeiras, o seu nome - Django fará o trabalho por nós para ajudar a assegurar que a base de dados seja actualizada da forma que pretendíamos.

É importante notar que Django pode não fazer estas alterações por uma série de razões. Para os novos criadores de Django, isto deve-se quase sempre à adição e remoção de campos e colunas e à não execução correcta das migrações.

Quando feito correctamente, python manage.py migrate assegura um sistema estável que combina o nosso código Python com as nossas tabelas SQL, permitindo-nos assim todas as maravilhas que tanto as bases de dados Django como as bases de dados SQL nos proporcionam.

Como é que isto nos dá mais flexibilidade?

Python tem aplicações amplas onde a SQL não o faz. A Structured Query Language tem as suas limitações escritas no nome. Quem está a criar animações Pixar apenas com SQL?

OK, tudo isto é para dizer que a simplicidade de Python ajuda de facto os programadores a adoptar o poder de SQL e possivelmente sem sequer o saberem.

Porque é que as bases de dados geridas e o Django fazem sentido

Quando se trata de criar uma aplicação web, incluindo o Django, terá de decidir algumas coisas:

  • Que solução(ões) de armazenamento de dados pretendemos? MySQL, Postgres, MongoDB, Redis, Object Storage, etc
  • Como vamos correr/integrar com a solução de armazenamento de dados?
  • Como vamos recuperar da interrupção ou do tempo de paragem?
  • Como vamos manter a solução de armazenamento?
  • Como vamos assegurar a nossa solução de armazenamento?
  • Como vamos fazer o backup da nossa solução de armazenamento?

As respostas a estas perguntas podem mudar à medida que o seu projecto cresce em complexidade, mas todas começam no mesmo lugar: decidir entre auto-gerido versus gerido por terceiros.

Auto-gerido:

  • Prós: Controlo e custo.
  • (Significativo) Con: É responsável por tudo.

Os serviços geridos custam muitas vezes mais dinheiro desde o início, enquanto que a auto-gestão significa que pode usar a sua distro preferida do Linux que é de alguma forma (ou de alguma forma) optimizada para o que precisa. Isto pode incluir correr uma versão bifurcada do MySQL que a sua equipa tenha modificado. Poderá poupar dólares na execução do seu serviço, mas isto levará sempre mais tempo a ser mantido.

Bases de dados geridas por terceiros: 

Sim, pode ser ligeiramente mais caro em dólares e cêntimos, mas demorará significativamente menos tempo a manter. Esta opção e as soluções de armazenamento de dados geridos são a minha escolha de facto para as minhas aplicações web. Neste exemplo, já estamos a utilizar o Django para gerir transacções de bases de dados. A SQLAlchemy também partilha esta força, uma vez que é utilizada com estruturas como FastAPI, Flask, e muitas outras. Se já está a subcontratar a sua escrita SQL a um pacote Python , porque não subcontratar a execução dos seus servidores SQL?

Agora, dada a eficácia de Python ORMs (como Django ORM e SQLAlchemy), recomendo-lhe que utilize bases de dados geridas e/ou serviços de armazenamento de dados geridos sempre que possível, eis o que pode ganhar se o fizer:

  • Redução do tempo de desenvolvimento
  • Redução do tempo de gestão
  • Redução do tempo de recuperação
  • Redução das interrupções de serviço
  • Redução da implementação e complexidade de desenvolvimento
  • Redução da complexidade nas migrações de bases de dados (de outros serviços)
  • Redução de actividades repetitivas/ineficazes/ineficientes para programadores SQL
  • Redução da complexidade DevOps/Ops
  • Aumento da eficácia dos desenvolvedores não-SQL
  • Aumento da implantação e da velocidade de desenvolvimento
  • Aumento da fiabilidade (muitas vezes apoiado por um Acordo de Nível de Serviço)
  • Aumento da segurança
  • Aumento da capacidade de manutenção
  • Aumento dos backups e redundância
  • Aumento marginal do custo

Eu fiz a lista acima com a mentalidade de usar um cluster de banco de dados MySQL gerenciado no Linode também Linode Object Storage (para armazenar arquivos como CSS, JavaScript, imagens, vídeos, etc). Na prática, o uso desses serviços nos ajuda a manter o foco na construção de uma excelente aplicação web com Django, FastAPI, Flask, Node.js, ou qualquer outro. Dito de outra forma, mudamos o foco para a construção das ferramentas e software que os seus utilizadores realmente querem. Sabe, onde está o valor real para eles.

MySQL, PostgreSQL, Redis, e Django

Durante muito tempo, a principal base de dados de Django foi o PostgreSQL. Eu diria que isto se deve, em grande parte, ao facto de se poder utilizar um campo JSON apenas dentro do Postgres. Com Django 3.2+ e MySQL 5.7.8+, o JSONField está agora disponível também para o MySQL.

Porque é que isto é importante?

O armazenamento de dados não estruturados, como o JSON, é frequentemente necessário quando se lida com conteúdo gerado pelo utilizador ou quando se armazenam dados de outros API serviços. Vamos ver como:

from django.db import models

class Pet(models.Model):
    name = models.CharField(max_length=200)
    data = models.JSONField(null=True)

    def __str__(self):
        return self.name

Aqui estão os dados que quero armazenar em relação a este animal de estimação:

pet1 = {
    "name": "Bruno",
    "type": "Rat",
    "nickname": "We don't talk about it",
    "age": 2,
    "age_interval": "months"
}

pet2 = {
    "name": "Tom",
    "type": "Cat",
    "breed": "Mixed"
    "age": 4,
    "age_interval: "years",
    "favorite_food": [{"brand": "Acme", "flavor": "Tuna" }]
}

pet3 = {
    "name": "Stewey",
    "type": "Dog",
    "breed": "unknown"
    "age": 34,
    "age_interval: "dog years",
    "nickname": "Football"
}

Estes dados mostram-nos quando podemos precisar de um JSONField . Podemos armazenar todos os nomes dos animais de estimação (utilizando o name chave) e manter o resto a ser armazenado no JSONField. O que é fixe em JSONFields é que eles podem ser consultados como qualquer outro campo Django padrão, mesmo com estes esquemas variáveis.

Há um debate contínuo entre os criadores Django sobre qual a base de dados a utilizar: MySQL ou PostgreSQL. Durante muito tempo, optei sempre pelo PostgreSQL devido ao facto de o JSONField só estar disponível no PostgreSQL, e já não é esse o caso. Eu digo para escolher um e ficar com ele até que não sirva mais as suas necessidades.

Mas para que é que usamos Redis?

Redis é uma datastore in-memory que é incrivelmente rápida e frequentemente utilizada como base de dados temporária (mais sobre isto num segundo), um serviço de cache, e/ou uma fila de mensagens. A razão pela qual lhe chamo uma base de dados temporária deve-se ao facto de ser in-memory. A memória é muitas vezes mais cara do que o armazenamento em disco, pelo que o armazenamento de dados a longo prazo em memória não é muitas vezes viável.

O meu caso de utilização principal para Redis e Django são o caching e a fila de espera

Caching: Digamos que tem várias páginas web que os utilizadores visitam muito. Quer que os dados nessas páginas sejam mostrados aos utilizadores o mais rapidamente possível. Redis, como um sistema de cache para Django, torna isto incrivelmente fácil. Os dados dentro destas páginas podem ser renderizados a partir de uma base de dados SQL, mas Redis pode armazenar os dados renderizados a partir da cache. Por outras palavras, utilizar Redis com SQL pode muitas vezes acelerar as suas respostas ao mesmo tempo que reduz a quantidade de consultas às suas bases de dados SQL.

Enfileiramento: Outro caso de uso popular da Redis é a descarga de tarefas de longa duração para outro processo (muitas vezes através de um pacote Python chamado Celery). Quando precisar de o fazer, pode usar o Redis como fila de espera das tarefas que devem ser concluídas noutra altura.

Por exemplo, se tiver um utilizador que precise de um relatório de todas as suas transacções durante os últimos cinco anos, o software pode levar horas para gerar efectivamente esse relatório. Obviamente, ninguém vai ficar a olhar para uma máquina durante horas. Assim, descarregaríamos este pedido do nosso utilizador para uma fila Redis. Uma vez em Redis, podemos ter um processo de trabalhador em execução (como a utilização de aipo com Django) para gerar efectivamente o relatório. Uma vez feito o relatório, não importa quanto tempo demorasse, o utilizador seria notificado. Esta notificação, tal como com outras notificações, também poderia ser feita através de uma Redis Queue acoplada a um processo de trabalhador com Aipo/Django.

Isto tudo para dizer que o Redis e o MySQL se complementam muito bem. Você pode implantar um servidor de banco de dados Redis auto-gerenciado através do Linode Marketplace.

Object Storage

O último serviço gerenciado relacionado a dados que eu recomendo usar é o Linode Object Storage. Object Storage é responsável por todos os outros tipos de dados que você pode precisar armazenar. Por exemplo, não armazenaríamos todos os bytes de um vídeo no MySQL. Em vez disso, armazenaríamos metadados relacionados a esse vídeo e armazenaríamos o vídeo em Object Storage.

Aqui estão algumas coisas para as quais utilizaria o armazenamento de objectos:

  • Folhas de estilo em cascata (CSS)
  • JavaScript (como React.js, Vue.js, Vanilla.js, etc)
  • Vídeos
  • Images (em bruto e comprimido)
  • CSV, XLSX
  • Base de dados Backups
  • Camadas de imagem de contentor de doca (se auto-geridas)
  • Iterações de Algoritmos de Aprendizagem de Máquina Formada
  • Terraform Ficheiros do Estado
  • PDFs (tanto grandes como pequenos)
  • Qualquer ficheiro persistente que precise de ser descarregado frequentemente (ou carregado)

Resumo

Depois de ler isto, espero que se sinta motivado para alavancar o poder dos serviços geridos com os seus projectos de aplicações web. Django é uma excelente solução para construir aplicações web em cima de bases de dados SQL, mas não é certamente a única. Se quiserem mergulhar no interior dos servidores SQL e SQL, penso que é um exercício que vale a pena ver quantas aplicações de sucesso alavancam Django para lidar com a maior parte do que Django pode fazer.

Aqui estão alguns (ou muitos) destaques que tornam Django com Managed MySQL em Linode espantoso:

  • Django faz o levantamento SQL pesado para si (assim como ferramentas como SQLAlchemy for Flask/FastAPI)
  • Django também permite comandos SQL em bruto (mais uma vez, assim como ferramentas como SQLAlchemy)
  • Django ajuda os principiantes a aprender os comandos SQL
  • Django tem suporte integrado para MySQL e PostgreSQL (para além de um cliente python específico para db)
  • Aumenta a velocidade para as instalações de produção
  • Aumento da fiabilidade e recuperabilidade
  • Permite que os ambientes de desenvolvimento e produção correspondam quase exactamente à tecnologia das bases de dados
  • Torna o Django baseado em contentores mais fácil e mais fiável
  • Desbloqueia o escalonamento de uma implantação de um único nó para uma transição de vários nós ou mesmo de uma transição total para Kubernetes
  • Mais fácil para os novos criadores Django/Python utilizarem sistemas de grau de produção
  • A partilha de bases de dados através de múltiplas aplicações baseadas em python é mais fácil e mais segura (tal como uma aplicação FastAPI de/para uma base de dados MySQL baseada em Django).
  • Django's JSONField agora suportado usando MySQL (anteriormente apenas PostgreSQL)
  • Fácil de testar (durante CI/CD ou em ambientes de desenvolvimento local)
  • Balanças para satisfazer as exigências de Django
  • Apoio a múltiplas bases de dados num único projecto Django tal como: utilização do MySQL como base de dados primária de leitura/escrita e uma base de dados de réplicas de leitura MySQL para consultas comuns.
  • Controlos de Acesso Rigorosos (Linode Private IPs, desenvolvimento local)
  • Requer Certificado SSL para ligação (adiciona complexidade às implementações mas também aumenta a segurança)
  • Permite a ligação privada (na mesma região; reduz os custos de ligação)

Se estiver interessado numa abordagem moderna de implantação de aplicações Django em Linode juntamente com uma base de dados MySQL gerida, acções GitHub para CI/CD, Terraform, e Ansible, salte para toneladas de conteúdo educativo passo-a-passo gratuito:

Para o ajudar a começar, o Código para Empresários GitHub tem um repositório de código que acompanha cada passo da série. Boa sorte, e não deixe de me informar como as coisas estão a correr através do Twitter @JustinMitchel.


Comentários

Deixe uma resposta

O seu endereço de correio electrónico não será publicado. Os campos obrigatórios estão marcados com *