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 para obter detalhes completos. documentação oficial do Django 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 , etc. Certifique-se de fazer o melhor uso deles. len() count() 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: Para definir o backend de cache a ser usado. BACKEND: valores em que é o endereço IP do daemon do Memcached e é a porta na qual o Memcached está sendo executado ou a URL que aponta para sua instância do Redis, usando o esquema apropriado. LOCATION: ip:port ip port Para habilitar o cache do banco de dados com o Memcached, instale usando o pip usando o seguinte comando: pymemcache pip install pymemcache Em seguida, você pode definir as configurações de cache em seu da seguinte maneira: settings.py 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 adicionando o seguinte código: settings.py 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 , ele não verifica o cache e lê os resultados diretamente do banco de dados, nem salva os resultados no queryset. iterator() 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 , o Django manterá a conexão SQL aberta e lerá cada registro e chamará antes de ler o próximo registro: iterator() do_something() 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 , 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. CONN_MAX_AGE Você precisa agrupar suas opções extras em , conforme detalhado no : OPTIONS 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. Essas expressões são definidas em e , mas por conveniência, elas estão disponíveis e normalmente importadas de . Nota: django.db.models.expressions django.db.models.aggregates django.db.models Expressão F Na API do Django Queryset, as expressões 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 para produzir uma frase SQL que define a atividade necessária do banco de dados. F() F() 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 , podemos fazer isso em uma única consulta da seguinte forma: F() from django.db.models import F Product.objects.update(price=F('price') * 1.2) Usando e select_related() prefetch_related() O Django fornece os argumentos e para otimizar seus conjuntos de consultas minimizando o número de solicitações do banco de dados. select_related() prefetch_related() De acordo com a documentação oficial do Django: "segue" relacionamentos de chave estrangeira, selecionando dados adicionais de objetos relacionados ao executar sua consulta. select_related() faz uma pesquisa separada para cada relacionamento e faz a "junção" em Python. prefetch_related() select_related() Usamos quando o item a ser selecionado é um único objeto, o que significa um campo , e um campo . select_related() ForeignKey OneToOne OneToOne Você pode usar 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, recupera quaisquer dados extras de objetos relacionados de relacionamentos de chave estrangeira. select_related() select_related() funciona gerando uma junção SQL e inclui as colunas do objeto relacionado na expressão . Como resultado, retorna itens relacionados na mesma consulta de banco de dados. select_related() SELECT select_related() Embora 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. select_related() A sintaxe simplesmente se parece com isso: queryset = Tweet.objects.select_related('owner').all() prefetch_related() Em contraste, é 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. prefetch_related() 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 e em detalhes . select_related() prefetch_related() aqui Usando e bulk_create() bulk_update() é um método que cria a lista de objetos fornecida no banco de dados com uma consulta. Da mesma forma, é um método que atualiza os campos fornecidos nas instâncias de modelo fornecidas com uma consulta. bulk_create() bulk_update() 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 assim: bulk_create() #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 assim: bulk_update() 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!