Entendendo a importância da otimização de Banco de Dados
Um dos principais desafios enfrentados por desenvolvedores Web é criar aplicações que respondam de forma rápida e eficiente às solicitações dos usuários. E quando se trata de projetos Symfony, é fundamental que a performance do Banco de Dados seja otimizada para garantir a melhor experiência ao usuário.
Por que é importante otimizar o Banco de dados em projetos Symfony?
Otimizar o Banco de dados é essencial para melhorar a performance de qualquer aplicação Web. Isso porque o Banco de Dados é um dos principais componentes de um projeto Symfony e, caso ele não esteja adequadamente otimizado, a aplicação pode se tornar lenta, não escalável e com baixa eficiência.
Práticas para otimizar a performance de Banco de Dados em projetos Symfony
Confira a seguir algumas práticas essenciais para otimizar a performance de Banco de Dados em projetos Symfony:
1. Use indexes para melhorar a performance de consultas SQL
Indexes ajudam a melhorar a performance de consultas SQL. Certifique-se de ter indexes definidos em todas as colunas que são utilizadas na cláusula WHERE, JOIN ou ORDER BY de suas consultas SQL.
2. Utilize o Doctrine Query Cache
O Doctrine Query Cache permite armazenar em cache os resultados de consultas SQL, reduzindo significativamente o número de consultas SQL executadas e melhorando a performance da aplicação. Você pode habilitar o Doctrine Query Cache adicionando a seguinte linha ao arquivo de configuração do Doctrine:
doctrine:
orm:
result_cache_driver:
type: memcached
host: localhost
port: 11211
...
3. Utilize a técnica de Eager Loading
A técnica de Eager Loading permite reduzir o número de consultas SQL executadas pelo Doctrine. Isso pode ser feito adicionando a opção “fetch” nas relações do Doctrine:
// Exemplo de utilização do Eager Loading
$user = $entityManager->getRepository(User::class)->findOneById($userId, ['groups' => 'user_with_posts']);
4. Utilize transações para evitar problemas de concorrência
Utilizar transações é uma forma eficiente de evitar problemas de concorrência, garantindo que as operações no Banco de Dados sejam executadas de forma atômica. Para utilizar transações, basta utilizar o método “beginTransaction()” de sua instância do EntityManager do Doctrine:
// Exemplo de utilização de transações
$entityManager->beginTransaction();
try {
$entityManager->persist($entity1);
$entityManager->persist($entity2);
$entityManager->flush();
$entityManager->commit();
} catch (Exception $e) {
$entityManager->rollback();
}
5. Utilize o Cache de Segundo Nível do Doctrine
O Cache de Segundo Nível do Doctrine permite armazenar em cache as entidades e metadados do Doctrine, reduzindo significativamente a quantidade de consultas SQL executadas na inicialização da aplicação e melhorando a performance da aplicação. Você pode habilitar o Cache de Segundo Nível do Doctrine adicionando a seguinte linha ao arquivo de configuração do Doctrine:
doctrine:
orm:
metadata_cache_driver:
type: memcached
host: localhost
port: 11211
result_cache_driver:
type: memcached
host: localhost
port: 11211
...
6. Evite Consultas SQL Pesadas
Consultas SQL Pesadas são consultas que levam muito tempo para serem executadas e que podem prejudicar a performance da aplicação. Evite consultas SQL Pesadas utilizando LIMIT, OFFSET ou PAGINATION em suas consultas SQL.
7. Utilize o Doctrine Pagination
O Doctrine Pagination permite paginar os resultados de suas consultas SQL, reduzindo significativamente o número de resultados retornados e melhorando a performance da aplicação. Para utilizar o Doctrine Pagination, basta adicionar a opção “paginate” em suas consultas SQL:
// Exemplo de utilização do Doctrine Pagination
$query = $entityManager->createQuery('SELECT p FROM AppBundle:Post p WHERE p.user = :user ORDER BY p.createdAt DESC')->setParameter('user', $user);
$paginator = new Paginator($query, $fetchJoinCollection = true);
$paginator->getQuery()
->setFirstResult(0)
->setMaxResults(10);
$paginator->count();
8. Utilize o Doctrine Result Cache
O Doctrine Result Cache permite armazenar em cache os resultados de consultas SQL, reduzindo significativamente o número de consultas SQL executadas e melhorando a performance da aplicação. Para habilitar o Doctrine Result Cache, basta adicionar a opção “result_cache_lifetime” em suas consultas SQL:
// Exemplo de utilização do Doctrine Result Cache
$query = $entityManager->createQuery('SELECT p FROM AppBundle:Post p WHERE p.user = :user ORDER BY p.createdAt DESC')->setParameter('user', $user)->useResultCache(true, 3600);
$posts = $query->getResult();
9. Utilize o Doctrine Native Query
O Doctrine Native Query permite executar consultas SQL diretas no Banco de Dados, reduzindo significativamente o número de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o Doctrine Native Query, basta utilizar o método “createNativeQuery” de sua instância do EntityManager do Doctrine:
// Exemplo de utilização do Doctrine Native Query
$rsm = new ResultSetMappingBuilder($entityManager);
$rsm->addRootEntityFromClassMetadata('AppBundle:Post', 'p');
$query = $entityManager->createNativeQuery('SELECT p.* FROM post p WHERE p.user_id = :user_id ORDER BY p.created_at DESC', $rsm)->setParameter('user_id', $userId);
$posts = $query->getResult();
10. Utilize o Doctrine Proxy
O Doctrine Proxy permite carregar as entidades do Doctrine de forma preguiçosa, reduzindo significativamente a quantidade de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o Doctrine Proxy, basta adicionar a opção “lazy” em suas propriedades anotadas no Doctrine:
// Exemplo de utilização do Doctrine Proxy
/**
* @ORMManyToOne(targetEntity="AppBundleEntityUser", inversedBy="posts")
* @ORMJoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
* @ORMProxy(lazy=true)
*/
private $user;
11. Utilize o Doctrine Criteria
O Doctrine Criteria permite definir critérios dinâmicos para suas consultas SQL, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Criteria, basta criar uma instância do Criteria e adicioná-lo à sua consulta SQL:
// Exemplo de utilização do Doctrine Criteria
$criteria = Criteria::create()
->orderBy(['createdAt' => 'DESC'])
->setMaxResults(10);
$posts = $entityManager->getRepository(Post::class)->matching($criteria);
12. Utilize o Doctrine Connection
O Doctrine Connection permite executar consultas SQL diretas no Banco de Dados, reduzindo significativamente a quantidade de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o Doctrine Connection, basta criar uma instância do Connection e executar sua consulta SQL:
// Exemplo de utilização do Doctrine Connection
$connection = $entityManager->getConnection();
$stmt = $connection->prepare('SELECT * FROM post WHERE user_id = :user_id ORDER BY created_at DESC');
$stmt->bindValue('user_id', $userId);
$stmt->execute();
$posts = $stmt->fetchAll();
13. Utilize o Doctrine Batch Processing
O Doctrine Batch Processing permite processar grandes quantidades de dados em lotes, reduzindo significativamente a complexidade de suas operações e melhorando a performance da aplicação. Para utilizar o Doctrine Batch Processing, basta criar uma instância do BatchProcessor e executar sua operação:
// Exemplo de utilização do Doctrine Batch Processing
$batchProcessor = $entityManager->getUnitOfWork()->getBatchProcessor();
$batchProcessor->process($entities);
14. Utilize o Doctrine Named Query
O Doctrine Named Query permite definir consultas SQL com nomes, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Named Query, basta adicionar a anotação “@NamedQuery” em suas classes do Doctrine:
// Exemplo de utilização do Doctrine Named Query
/**
* @Entity
* @Table(name="post")
* @NamedQuery(name="findLatestPosts", query="SELECT p FROM AppBundle:Post p WHERE p.user = :user ORDER BY p.createdAt DESC")
*/
class Post
{
...
}
15. Utilize o Doctrine Query Builder
O Doctrine Query Builder permite criar consultas SQL utilizando funções orientadas a objetos, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Query Builder, basta criar uma instância do QueryBuilder e definir seus critérios:
// Exemplo de utilização do Doctrine Query Builder
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('p')
->from(Post::class, 'p')
->where('p.user = :user')
->orderBy('p.createdAt', 'DESC')
->setParameter('user', $user);
$posts = $queryBuilder->getQuery()->getResult();
16. Utilize o Doctrine Persistent Collection
O Doctrine Persistent Collection permite acessar coleções de entidades do Doctrine de forma preguiçosa, reduzindo significativamente a quantidade de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o Doctrine Persistent Collection, basta criar uma instância do PersistentCollection e adicionar suas entidades:
// Exemplo de utilização do Doctrine Persistent Collection
$collection = new ArrayCollection([$entity1, $entity2, $entity3]);
$persistentCollection = new PersistentCollection($entityManager, Article::class, $collection);
17. Utilize o Doctrine DateTime Type
O Doctrine DateTime Type permite manipular datas e horários no formato de Banco de Dados, reduzindo significativamente a complexidade de suas operações e melhorando a performance da aplicação. Para utilizar o Doctrine DateTime Type, basta adicionar a opção “custom_types” em suas configurações do Doctrine:
// Exemplo de utilização do Doctrine DateTime Type
doctrine:
dbal:
types:
datetime:
class: DoctrineDBALTypesDateTimeType
commented: true
datetime_immutable:
class: DoctrineDBALTypesDateTimeImmutableType
commented: true
dateinterval:
class: MyDBALTypesDateIntervalType
commented: true
18. Utilize o Doctrine UUID Type
O Doctrine UUID Type permite manipular identificadores no formato UUID, reduzindo significativamente a complexidade de suas operações e melhorando a performance da aplicação. Para utilizar o Doctrine UUID Type, basta adicionar a opção “custom_types” em suas configurações do Doctrine:
// Exemplo de utilização do Doctrine UUID Type
doctrine:
dbal:
types:
uuid_binary_ordered_time:
class: RamseyUuidDoctrineUuidBinaryOrderedTimeType
commented: true
orm:
dql:
string_functions:
UUID_BINARY_ORDERED_TIME: RamseyDoctrineUuidDoctrineFunctionsUuidBinaryOrderedTimeFunction
numeric_functions:
TIME_TO_SEC: DoctrineExtensionsQueryMysqlTimeToSec
SEC_TO_TIME: DoctrineExtensionsQueryMysqlSecToTime
datetime_functions:
STR_TO_DATE: DoctrineExtensionsQueryMysqlStrToDate
19. Utilize o Doctrine Model Generator
O Doctrine Model Generator permite gerar automaticamente classes de entidade do Doctrine, reduzindo significativamente a complexidade de suas operações e melhorando a performance da aplicação. Para utilizar o Doctrine Model Generator, basta adicionar a opção “generate:entities” em suas configurações do Symfony:
// Exemplo de utilização do Doctrine Model Generator
php bin/console generate:entities AppBundle/Entity
20. Utilize o Doctrine Dynamic Filters
O Doctrine Dynamic Filters permite definir filtros dinâmicos para suas consultas SQL, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Dynamic Filters, basta criar uma instância do DynamicFilter e definir seus critérios:
// Exemplo de utilização do Doctrine Dynamic Filters
$entityManager->getConfiguration()->addFilter('status', 'AppBundleFilterPostStatusFilter');
$entityManager->getFilters()->enable('status');
class PostStatusFilter extends SQLFilter
{
/**
* @param ClassMetadata $targetEntity
* @param string $targetTableAlias
*
* @return string
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if ($targetEntity->getName() !== Post::class) {
return '';
}
return $targetTableAlias.'.status = "published"';
}
}
21. Utilize o Doctrine Events
O Doctrine Events permite definir eventos para suas entidades do Doctrine, reduzindo significativamente a complexidade de suas operações e melhorando a performance da aplicação. Para utilizar o Doctrine Events, basta definir seus métodos de callback em suas classes do Doctrine:
// Exemplo de utilização do Doctrine Events
/**
* @ORMEntityListeners("AppBundleEventListenerPostEntityListener")
*/
class Post
{
...
}
class PostEntityListener
{
/**
* @ORMPostPersist
*
* @param Post $post
*/
public function onPostPersist(Post $post)
{
// ...
}
}
22. Anote suas entidades com @Cache
O @Cache permite armazenar em cache seus objetos do Doctrine, reduzindo significativamente a quantidade de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o @Cache, basta adicionar a anotação em suas classes do Doctrine:
// Exemplo de utilização do @Cache
/**
* @ORMEntity
* @Cache(usage="NONSTRICT_READ_WRITE")
*/
class Post
{
...
}
23. Utilize o Doctrine Transactions
O Doctrine Transactions permite executar operações do Doctrine de forma atômica, garantindo a integridade de seus dados e melhorando a performance da aplicação. Para utilizar o Doctrine Transactions, basta utilizar o método “beginTransaction” de sua instância do EntityManager do Doctrine:
// Exemplo de utilização do Doctrine Transactions
try {
$entityManager->beginTransaction();
$post = $entityManager->getRepository(Post::class)->findOneById($postId);
$post->setUpdatedAt(new DateTime());
$entityManager->flush();
$entityManager->commit();
} catch (Exception $e) {
$entityManager->rollback();
}
24. Utilize o Doctrine Cache
O Doctrine Cache permite armazenar em cache seus objetos do Doctrine, reduzindo significativamente a quantidade de operações do Doctrine e melhorando a performance da aplicação. Para utilizar o Doctrine Cache, basta adicionar a opção “cache_driver” em suas configurações do Doctrine:
// Exemplo de utilização do Doctrine Cache
doctrine:
orm:
cache_driver:
type: memcached
host: localhost
port: 11211
25. Utilize o Doctrine Hydration Modes
O Doctrine Hydration Modes permite definir modos de hidratação para suas consultas SQL, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Hydration Modes, basta adicionar a opção “hydrationMode” em suas configurações do Doctrine:
// Exemplo de utilização do Doctrine Hydration Modes
$query = $entityManager->createQuery('SELECT p FROM AppBundle:Post p');
$query->setHydrationMode(Query::HYDRATE_ARRAY);
$posts = $query->getResult();
26. Utilize o Doctrine Query Logging
O Doctrine Query Logging permite monitorar o desempenho de suas consultas SQL, reduzindo significativamente a complexidade de suas consultas SQL e melhorando a performance da aplicação. Para utilizar o Doctrine Query Logging, basta adicionar a opção “logging” em suas configurações do Doctrine:
// Exemplo de utilização do Doctrine Query Logging
doctrine:
dbal:
logging: true
27. Utilize o Doctrine Hydrator
O Doctrine Hydrator permite hidr