paint-brush
7 melhores práticas de otimização de banco de dados para desenvolvedores Djangopor@pragativerma
5,241 leituras
5,241 leituras

7 melhores práticas de otimização de banco de dados para desenvolvedores Django

por Pragati Verma9m2022/08/30
Read on Terminal Reader
Read this story w/o Javascript

Muito longo; Para ler

O gerenciamento de banco de dados é um dos aspectos mais cruciais do desenvolvimento de back-end. Um banco de dados devidamente otimizado pode ajudar a reduzir o tempo de resposta e, portanto, levar a uma melhor experiência do usuário. Compreender os querysets no Django é a chave para a otimização, portanto, lembre-se do seguinte: Querysets são preguiçosos, o que significa que nenhuma solicitação de banco de dados correspondente é feita até que você execute certas ações no queryset, como iterar sobre ele. A indexação de banco de dados é uma técnica para acelerar as consultas durante a recuperação de registros de um banco de dados.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - 7 melhores práticas de otimização de banco de dados para desenvolvedores Django
Pragati Verma HackerNoon profile picture


O gerenciamento de banco de dados é um dos aspectos mais cruciais do desenvolvimento de back-end. Um banco de dados devidamente otimizado pode ajudar a reduzir o tempo de resposta e, portanto, levar a uma melhor experiência do usuário.


Neste artigo, discutiremos as maneiras de otimizar o banco de dados para velocidade em aplicativos Django. Embora não iremos nos aprofundar em cada conceito individualmente, portanto, consulte a documentação oficial do Django para obter detalhes completos.


Entendendo as consultas no Django

Compreender os conjuntos de consultas no Django é a chave para a otimização, portanto, lembre-se do seguinte:


  • Querysets são preguiçosos, o que significa que nenhuma solicitação de banco de dados correspondente é feita até que você execute determinadas ações no queryset, como iterar sobre ele.
  • Sempre limite o resultado de uma consulta ao banco de dados especificando o número de valores a serem retornados.
  • No Django, querysets podem ser avaliados por iteração, divisão, cache e métodos python como len() , count() etc. Certifique-se de fazer o melhor uso deles.
  • Os conjuntos de consultas do Django são armazenados em cache, de modo que, se você reutilizar o mesmo conjunto de consultas, várias solicitações de banco de dados não serão feitas, minimizando assim o acesso ao banco de dados.
  • Recupere tudo o que você precisa de uma vez, mas certifique-se de recuperar apenas o que precisa.


Otimização de consultas em Django

Indexação de banco de dados

A indexação de banco de dados é uma técnica para acelerar as consultas durante a recuperação de registros de um banco de dados. À medida que o aplicativo aumenta de tamanho, ele pode ficar lento e os usuários perceberão, pois levará muito mais tempo para obter os dados necessários. Assim, a indexação é uma operação inegociável quando se trabalha com grandes bases de dados que geram um grande volume de dados.


A indexação é um método de classificação de um grande número de dados com base em vários campos. Ao criar um índice em um campo em um banco de dados, você cria outra estrutura de dados que contém o valor do campo, bem como um ponteiro para o registro ao qual está relacionado. Essa estrutura de índice é então classificada, tornando possíveis as pesquisas binárias.


Por exemplo, aqui está um modelo Django chamado Sale:


 # models.py from django.db import models class Sale(models.Model): sold_at = models.DateTimeField( auto_now_add=True, ) charged_amount = models.PositiveIntegerField()


A indexação de banco de dados pode ser adicionada a um campo específico ao definir um modelo Django da seguinte maneira:


 # models.py from django.db import models class Sale(models.Model): sold_at = models.DateTimeField( auto_now_add=True, db_index=True, #DB Indexing ) charged_amount = models.PositiveIntegerField()


Se você executar as migrações para este modelo, o Django criará um índice de banco de dados na tabela Sales e ficará bloqueado até que o índice seja concluído. Em uma configuração de desenvolvimento local, com pouca quantidade de dados e pouquíssimas conexões, essa migração pode parecer instantânea, mas quando falamos em ambiente de produção, existem grandes conjuntos de dados com muitas conexões simultâneas que podem causar indisponibilidade como obter um bloqueio e criar um índice de banco de dados pode levar muito tempo.


Você também pode criar um único índice para dois campos, conforme mostrado abaixo:


 # models.py from django.db import models class Sale(models.Model): sold_at = models.DateTimeField( auto_now_add=True, db_index=True, #DB Indexing ) charged_amount = models.PositiveIntegerField() class Meta: indexes = [ ["sold_at", "charged_amount"]]


Cache do banco de dados

O cache do banco de dados é uma das melhores abordagens para obter uma resposta rápida de um banco de dados. Ele garante que menos chamadas sejam feitas ao banco de dados, evitando sobrecarga. Uma operação de cache padrão segue a estrutura abaixo:



O Django fornece um mecanismo de cache que pode usar diferentes back-ends de cache, como Memcached e Redis, que permitem evitar a execução das mesmas consultas várias vezes.


O Memcached é um sistema de memória de código aberto que garante o fornecimento de resultados armazenados em cache em menos de um milissegundo. É simples de configurar e dimensionar. O Redis, por outro lado, é uma solução de cache de código aberto com características semelhantes ao Memcached. A maioria dos aplicativos offline emprega dados previamente armazenados em cache, o que significa que a maioria das consultas nunca chega ao banco de dados.


As sessões do usuário devem ser salvas em um cache em seu aplicativo Django e, como o Redis mantém os dados em disco, todas as sessões para usuários conectados são originadas do cache e não do banco de dados.


Para usar Memcache com Django, precisamos definir o seguinte:

  • BACKEND: Para definir o backend de cache a ser usado.
  • LOCATION: valores ip:port em que ip é o endereço IP do daemon do Memcached e port é a porta na qual o Memcached está sendo executado ou a URL que aponta para sua instância do Redis, usando o esquema apropriado.


Para habilitar o cache do banco de dados com o Memcached, instale pymemcache usando o pip usando o seguinte comando:


 pip install pymemcache


Em seguida, você pode definir as configurações de cache em seu settings.py da seguinte maneira:


 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', 'LOCATION': '127.0.0.1:11211', } }


No exemplo acima, o Memcached está sendo executado na porta 11211 do host local (127.0.0.1), usando a ligação pymemcache :


Da mesma forma, para habilitar o cache do banco de dados usando o Redis, instale o Redis usando o pip usando o comando abaixo:


 pip install redis


Em seguida, defina as configurações de cache em seu settings.py adicionando o seguinte código:


 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379', } }


Memcached e Redis também podem ser usados para armazenar tokens de autenticação do usuário. Como cada pessoa que efetua login deve fornecer um token, todos esses procedimentos podem resultar em sobrecarga significativa do banco de dados. O uso de tokens em cache resultará em acesso ao banco de dados consideravelmente mais rápido.


Usando o Iterator quando possível

Um queryset no Django, normalmente, armazena em cache seu resultado quando a avaliação acontece e para qualquer operação posterior com esse queryset, ele primeiro verifica se há resultados em cache. No entanto, quando você usa iterator() , ele não verifica o cache e lê os resultados diretamente do banco de dados, nem salva os resultados no queryset.


Agora, você deve estar se perguntando como isso é útil. Considere um queryset que retorna um grande número de objetos com muita memória para armazenar em cache, mas deve ser usado apenas uma vez, nesse caso, você deve usar um iterator() .


Por exemplo, no código a seguir, todos os registros serão buscados no banco de dados e, em seguida, carregados na memória e, em seguida, faremos uma iteração em cada um deles:


 queryset = Product.objects.all() for each in queryset: do_something(each)


Considerando que, se usarmos iterator() , o Django manterá a conexão SQL aberta e lerá cada registro e chamará do_something() antes de ler o próximo registro:


 queryset = Product.objects.all().iterator() for each in queryset: do_something(each)


Usando uma conexão de banco de dados de persistência

O Django cria uma nova conexão de banco de dados para cada solicitação e a fecha após a conclusão da solicitação. Esse comportamento é causado por CONN_MAX_AGE , que tem um valor padrão de 0. Mas quanto tempo deve ser definido? Isso é determinado pelo volume de tráfego em seu site; quanto mais alto o volume, mais segundos são necessários para manter a conexão. Geralmente é recomendado começar com um número baixo, como 60.


Você precisa agrupar suas opções extras em OPTIONS , conforme detalhado no documentação :


 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'dashboard', 'USER': 'root', 'PASSWORD': 'root', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'CONN_MAX_AGE': '60', } } }


Usando expressões de consulta

As expressões de consulta definem um valor ou um cálculo que pode ser utilizado em uma operação de atualização, criação, filtragem, ordenação, anotação ou agregação.


Uma expressão de consulta interna comumente usada no Django é a expressão F. Vamos ver como funciona e pode ser útil.


Nota: Essas expressões são definidas em django.db.models.expressions e django.db.models.aggregates , mas por conveniência, elas estão disponíveis e normalmente importadas de django.db.models .


Expressão F

Na API do Django Queryset, as expressões F() são usadas para se referir diretamente aos valores do campo do modelo. Ele permite que você se refira a valores de campo de modelo e conduza ações de banco de dados neles sem ter que buscá-los no banco de dados e na memória Python. Em vez disso, o Django emprega o objeto F() para produzir uma frase SQL que define a atividade necessária do banco de dados.


Por exemplo, digamos que queremos aumentar o preço de todos os produtos em 20%, então o código ficaria mais ou menos assim:


 products = Product.objects.all() for product in products: product.price *= 1.2 product.save()


Porém, se utilizarmos F() , podemos fazer isso em uma única consulta da seguinte forma:


 from django.db.models import F Product.objects.update(price=F('price') * 1.2)


Usando select_related() e prefetch_related()

O Django fornece os argumentos select_related() e prefetch_related() para otimizar seus conjuntos de consultas minimizando o número de solicitações do banco de dados.


De acordo com a documentação oficial do Django:


select_related() "segue" relacionamentos de chave estrangeira, selecionando dados adicionais de objetos relacionados ao executar sua consulta.


prefetch_related() faz uma pesquisa separada para cada relacionamento e faz a "junção" em Python.


select_related()

Usamos select_related() quando o item a ser selecionado é um único objeto, o que significa um campo ForeignKey , OneToOne e um campo OneToOne .


Você pode usar select_related() para criar uma única consulta que retorne todos os objetos relacionados para uma única instância para conexões um-para-um e um-para-um. Quando a consulta é executada, select_related() recupera quaisquer dados extras de objetos relacionados de relacionamentos de chave estrangeira.


select_related() funciona gerando uma junção SQL e inclui as colunas do objeto relacionado na expressão SELECT . Como resultado, select_related() retorna itens relacionados na mesma consulta de banco de dados.


Embora select_related() produza uma consulta mais sofisticada, os dados adquiridos são armazenados em cache, portanto, o manuseio dos dados obtidos não requer nenhuma solicitação extra de banco de dados.


A sintaxe simplesmente se parece com isso:


 queryset = Tweet.objects.select_related('owner').all()


prefetch_related()

Em contraste, prefetch_related() é utilizado para conexões muitos-para-muitos e muitos-para-um. Ele produz uma única consulta que inclui todos os modelos e filtros fornecidos na consulta.


A sintaxe simplesmente se parece com isso:


 Book.objects.prefetch_related('author').get(id=1).author.first_name


NOTA: As relações ManyToMany não devem ser manipuladas usando SQL porque muitos problemas de desempenho podem aparecer ao lidar com tabelas grandes. É por isso que o método prefetch_related junta tabelas dentro do Python evitando grandes junções SQL.


Leia sobre a diferença entre select_related() e prefetch_related() em detalhes aqui .


Usando bulk_create() e bulk_update()

bulk_create() é um método que cria a lista de objetos fornecida no banco de dados com uma consulta. Da mesma forma, bulk_update() é um método que atualiza os campos fornecidos nas instâncias de modelo fornecidas com uma consulta.


Por exemplo, se tivermos um modelo de postagens como mostrado abaixo:


 class Post(models.Model): title = models.CharField(max_length=300, unique=True) time = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title


Agora, digamos que queremos adicionar vários registros de dados a este modelo, então podemos usar bulk_create() assim:


 #articles articles = [Post(title="Hello python"), Post(title="Hello django"), Post(title="Hello bulk")] #insert data Post.objects.bulk_create(articles)


E a saída ficaria assim:


 >>> Post.objects.all() <QuerySet [<Post: Hello python>, <Post: Hello django>, <Post: Hello bulk>]>


E se quisermos atualizar os dados, podemos usar bulk_update() assim:


 update_queries = [] a = Post.objects.get(id=14) b = Post.objects.get(id=15) c = Post.objects.get(id=16) #set update value a.title="Hello python updated" b.title="Hello django updated" c.title="Hello bulk updated" #append update_queries.extend((a, b, c)) Post.objects.bulk_update(update_queries, ['title'])


E a saída ficaria assim:


 >>> Post.objects.all() <QuerySet [<Post: Hello python updated>, <Post: Hello django updated>, <Post: Hello bulk updated>]>


Conclusão

Neste artigo, abordamos as dicas para otimizar o desempenho do banco de dados, reduzir gargalos e economizar recursos em uma aplicação Django.


Espero que tenha achado útil. Continue lendo!