Do not speak Portuguese? Translate this site with Google or Bing Translator
Como detectar consultas n+1 em PHP

Posted on: March 13, 2024 12:51 AM

Posted by: Renato

Views: 145

Como detectar consultas n+1 em PHP

 Qual é o problema de consulta n+1?

O problema de consulta n+1 é um problema comum de desempenho no desenvolvimento de software. Consultas N+1 resultam em muitas chamadas desnecessárias ao banco de dados. Isso pode fazer com que seu aplicativo tenha um desempenho semelhante ao de um caracol, especialmente à medida que os dados aumentam. Portanto, você deve estar ciente e abordar n+1 consultas para garantir que seus aplicativos sejam eficientes, responsivos e escaláveis.

Consultas N+1 ocorrem quando um aplicativo faz uma consulta ao banco de dados para recuperar um objeto e, então, para cada objeto recuperado, faz consultas adicionais para buscar objetos relacionados. Isso resulta em um total de N+1 consultas de banco de dados executadas para N objetos, o que pode reduzir significativamente a eficiência e o desempenho do aplicativo, especialmente ao lidar com grandes conjuntos de dados.

 Consultas N+1 ilustradas:

Vamos considerar um aplicativo de livraria que precisa exibir uma lista de autores e seus livros. O aplicativo pode primeiro consultar o banco de dados para recuperar todos os autores (1 consulta). Depois, para cada autor recuperado, faz outra consulta para buscar seus respectivos livros. Se houver 100 autores, isso resulta em 1 (consulta inicial) + 100 (uma para cada autor) = 101 consultas no total.

Isso é muito ineficiente e pode degradar gravemente o desempenho do aplicativo.

Para evitar o problema da consulta N+1, os desenvolvedores costumam usar técnicas como carregamento antecipado , onde os dados relacionados são carregados na própria consulta inicial do banco de dados, ou busca em lote , onde os dados associados a vários objetos são recuperados em lotes .

 Como você detecta n+1 consultas?

Se você tiver um aplicativo não trivial, provavelmente terá n+1 consultas. Se sua aplicação for construída com um framework web como Laravel ou Symfony e usar um ORM, você certamente terá muitas consultas n+1. Isso ocorre porque a camada ORM de muitas estruturas da web modernas carrega registros lentamente por padrão.

Problemas de consulta N+1 provavelmente passarão despercebidos em seus ambientes de desenvolvimento e teste. Ainda assim, eles podem prejudicar repentinamente o desempenho do aplicativo quando implantado em produção, onde o número de linhas no banco de dados é muito maior.

O sinal claro de que seu aplicativo tem n+1 consultas é um número incomumente alto de consultas ao banco de dados sendo executadas.

As consultas geralmente serão sequenciais e não sobrepostas.

Tudo isso é muito bom, mas você pode estar se perguntando: “Tudo bem, mas como posso saber quantas consultas estão sendo realizadas e se elas são 'sequenciais e não sobrepostas?'” Ótima pergunta! É aí que entram as ferramentas de monitoramento de desempenho de aplicativos (APM).

Usando Debugbar for Laravel ajuda.

Debugbar for Laravel

Unit Tests Packagist License Latest Stable Version Total Downloads Fruitcake

This is a package to integrate PHP Debug Bar with Laravel. It includes a ServiceProvider to register the debugbar and attach it to the output. You can publish assets and configure it through Laravel. It bootstraps some Collectors to work with Laravel and implements a couple custom DataCollectors, specific for Laravel. It is configured to display Redirects and (jQuery) Ajax Requests. (Shown in a dropdown) Read the documentation for more configuration options.

Debugbar Dark Mode screenshot

Note: Use the DebugBar only in development. Do not use Debugbar on publicly accessible websites, as it will leak information from stored requests (by design). It can also slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors.

This package includes some custom collectors:

  • QueryCollector: Show all queries, including binding + timing
  • RouteCollector: Show information about the current Route.
  • ViewCollector: Show the currently loaded views. (Optionally: display the shared data)
  • EventsCollector: Show all events
  • LaravelCollector: Show the Laravel version and Environment. (disabled by default)
  • SymfonyRequestCollector: replaces the RequestCollector with more information about the request/response
  • LogsCollector: Show the latest log entries from the storage logs. (disabled by default)
  • FilesCollector: Show the files that are included/required by PHP. (disabled by default)
  • ConfigCollector: Display the values from the config files. (disabled by default)
  • CacheCollector: Display all cache events. (disabled by default)

Bootstraps the following collectors for Laravel:

  • LogCollector: Show all Log messages
  • SymfonyMailCollector for Mail

And the default collectors:

  • PhpInfoCollector
  • MessagesCollector
  • TimeDataCollector (With Booting and Application timing)
  • MemoryCollector
  • ExceptionsCollector

It also provides a facade interface (Debugbar) for easy logging Messages, Exceptions and Time

Installation

Require this package with composer. It is recommended to only require the package for development.

composer require barryvdh/laravel-debugbar --dev
 

Laravel uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider.

The Debugbar will be enabled when APP_DEBUG is true.

If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders.

Laravel without auto-discovery:

If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php

Barryvdh\Debugbar\ServiceProvider::class,
 

If you want to use the facade to log messages, add this to your facades in app.php:

'Debugbar' => Barryvdh\Debugbar\Facades\Debugbar::class,
 

The profiler is enabled by default, if you have APP_DEBUG=true. You can override that in the config (debugbar.enabled) or by setting DEBUGBAR_ENABLED in your .env. See more options in config/debugbar.php You can also set in your config if you want to include/exclude the vendor files also (FontAwesome, Highlight.js and jQuery). If you already use them in your site, set it to false. You can also only display the js or css vendors, by setting it to 'js' or 'css'. (Highlight.js requires both css + js, so set to true for syntax highlighting)

Copy the package config to your local config with the publish command:

php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
 

Laravel with Octane:

Make sure to add LaravelDebugbar to your flush list in config/octane.php.

    'flush' => [
        \Barryvdh\Debugbar\LaravelDebugbar::class,
    ],
 

Lumen:

For Lumen, register a different Provider in bootstrap/app.php:

if (env('APP_DEBUG')) {
 $app->register(Barryvdh\Debugbar\LumenServiceProvider::class);
}
 

To change the configuration, copy the file to your config folder and enable it:

$app->configure('debugbar');
 

Usage

You can now add messages using the Facade (when added), using the PSR-3 levels (debug, info, notice, warning, error, critical, alert, emergency):

Debugbar::info($object);
Debugbar::error('Error!');
Debugbar::warning('Watch out…');
Debugbar::addMessage('Another message', 'mylabel');
 

And start/stop timing:

Debugbar::startMeasure('render','Time for rendering');
Debugbar::stopMeasure('render');
Debugbar::addMeasure('now', LARAVEL_START, microtime(true));
Debugbar::measure('My long operation', function() {
    // Do something…
});
 

Or log exceptions:

try {
    throw new Exception('foobar');
} catch (Exception $e) {
    Debugbar::addThrowable($e);
}
 

There are also helper functions available for the most common calls:

// All arguments will be dumped as a debug message
debug($var1, $someString, $intValue, $object);

// `$collection->debug()` will return the collection and dump it as a debug message. Like `$collection->dump()`
collect([$var1, $someString])->debug();

start_measure('render','Time for rendering');
stop_measure('render');
add_measure('now', LARAVEL_START, microtime(true));
measure('My long operation', function() {
    // Do something…
});
 

If you want you can add your own DataCollectors, through the Container or the Facade:

Debugbar::addCollector(new DebugBar\DataCollector\MessagesCollector('my_messages'));
//Or via the App container:
$debugbar = App::make('debugbar');
$debugbar->addCollector(new DebugBar\DataCollector\MessagesCollector('my_messages'));
 

By default, the Debugbar is injected just before </body>. If you want to inject the Debugbar yourself, set the config option 'inject' to false and use the renderer yourself and follow http://phpdebugbar.com/docs/rendering.html

$renderer = Debugbar::getJavascriptRenderer();
 

Note: Not using the auto-inject, will disable the Request information, because that is added After the response. You can add the default_request datacollector in the config as alternative.

Enabling/Disabling on run time

You can enable or disable the debugbar during run time.

\Debugbar::enable();
\Debugbar::disable();
 

NB. Once enabled, the collectors are added (and could produce extra overhead), so if you want to use the debugbar in production, disable in the config and only enable when needed.

Storage

Debugbar remembers previous requests, which you can view using the Browse button on the right. This will only work if you enable debugbar.storage.open in the config. Make sure you only do this on local development, because otherwise other people will be able to view previous requests. In general, Debugbar should only be used locally or at least restricted by IP. It's possible to pass a callback, which will receive the Request object, so you can determine access to the OpenHandler storage.

Twig Integration

Laravel Debugbar comes with two Twig Extensions. These are tested with rcrowe/TwigBridge 0.6.x

Add the following extensions to your TwigBridge config/extensions.php (or register the extensions manually)

'Barryvdh\Debugbar\Twig\Extension\Debug',
'Barryvdh\Debugbar\Twig\Extension\Dump',
'Barryvdh\Debugbar\Twig\Extension\Stopwatch',
 

The Dump extension will replace the dump function to output variables using the DataFormatter. The Debug extension adds a debug() function which passes variables to the Message Collector, instead of showing it directly in the template. It dumps the arguments, or when empty; all context variables.

{{ debug() }}
{{ debug(user, categories) }}
 

The Stopwatch extension adds a stopwatch tag similar to the one in Symfony/Silex Twigbridge.

{% stopwatch "foo" %}
    …some things that gets timed
{% endstopwatch %}

 Resolvendo n+1 consultas

Para ilustrar como resolver um problema de consulta N+1 em uma aplicação PHP usando um ORM (como Eloquent ou Doctrine do Laravel), revisitaremos o exemplo com base no cenário da aplicação de livraria mencionado anteriormente.

 Cenário:

Você tem um banco de dados com duas tabelas: autores e livros. Cada autor pode ter vários livros. Vamos primeiro ver o código que causa o problema da consulta N+1 e depois mostrarei como resolvê-lo.

Código com problema de consulta N+1:


 
// Retrieve all authors
$authors = Author::all();
 
foreach($authors as $author) {
   // For each author, retrieve their books$books = $author->books()->get(); // This causes an additional query for each author
   // Process the books...
}

Neste código, a primeira consulta recupera todos os autores, e então, para cada autor, uma nova consulta é executada para buscar seus livros, levando a n+1 consultas.

 Solução de consulta N+1 1: carregamento ansioso

Uma abordagem para resolver o problema de consulta n+1 é usar uma estratégia chamada carregamento antecipado. Com o carregamento antecipado, você carrega os modelos relacionados (neste caso, livros) em sua consulta inicial.


 
// Eager load books with authors in one query
$authors = Author::with('books')->get();
 
foreach($authors as $author) {
   // Now, no additional query is made here
   $books = $author->books;
   // Process the books...
}

 Solução de consulta N+1 2: consulta de junção

Às vezes, você pode querer usar uma instrução JOIN para buscar tudo em uma única consulta. Você pode fazer isso usando consultas brutas ou um construtor de consultas fornecido pela sua estrutura.

Exemplo de consulta bruta (usando PDO):


 
$sql = "SELECT * FROM authors JOIN books ON authors.id = books.author_id";
$stmt = $pdo->query($sql);
$authorsAndBooks = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Process the books accordingly

Exemplo de construtor de consultas (em Laravel):


 
$authorsAndBooks = DB::table('authors')
   ->join('books', 'authors.id', '=', 'books.author_id')
   ->get();
 
// Process the results accordingly

Usar uma consulta bruta ou um construtor de consultas permite escrever uma consulta SQL mais otimizada para buscar os dados necessários em uma única solicitação ao banco de dados.

 Solução de consulta N+1 3: cache

Ainda outra abordagem para resolver problemas de consulta n+1 é usar cache . O cache oferece uma solução estratégica para o problema de consulta n+1, principalmente quando os dados não mudam com frequência. Ao armazenar os resultados das consultas ao banco de dados em um cache, as solicitações subsequentes podem recuperar dados desse cache em vez de acessar o banco de dados novamente. Isto reduz significativamente o número de consultas feitas ao banco de dados, especialmente para solicitações repetidas.

Você deve observar que pode usar o cache junto com qualquer uma das soluções anteriores.

 Consultas N+1 em PHP: Conclusão

Compreender e abordar consultas n+1 é crucial para otimizar aplicativos PHP. Exploramos o que são consultas n+1, como elas podem degradar silenciosamente o desempenho do aplicativo e as estratégias para detectar problemas n+1 com ferramentas como Scout APM.

Resolver n+1 consultas não envolve apenas melhorar a velocidade; trata-se de escrever código mais inteligente e eficiente. Ao aplicar esses insights, você pode garantir uma experiência mais tranquila e rápida para seus usuários.

Fonte:

- https://laravel-news.com/how-to-detect-n1-queries-in-php?utm_content=buffer418ab&utm_medium=social&utm_source=linkedin.com&utm_campaign=buffer

- https://github.com/barryvdh/laravel-debugbar

 


2

Share

Donate to Site


About Author

Renato

Developer

Add a Comment
Comments 0 Comments

No comments yet! Be the first to comment

Blog Search


Categories

OUTROS (15) Variados (109) PHP (130) Laravel (158) Black Hat (3) front-end (28) linux (113) postgresql (39) Docker (27) rest (5) soap (1) webservice (6) October (1) CMS (2) node (7) backend (13) ubuntu (54) devops (25) nodejs (5) npm (2) nvm (1) git (8) firefox (1) react (6) reactnative (5) collections (1) javascript (6) reactjs (7) yarn (0) adb (1) Solid (2) blade (3) models (1) controllers (0) log (1) html (2) hardware (3) aws (14) Transcribe (2) transcription (1) google (4) ibm (1) nuance (1) PHP Swoole (5) mysql (31) macox (4) flutter (1) symfony (1) cor (1) colors (2) homeOffice (2) jobs (3) imagick (2) ec2 (1) sw (1) websocket (1) markdown (1) ckeditor (1) tecnologia (14) faceapp (1) eloquent (14) query (4) sql (40) ddd (3) nginx (9) apache (4) certbot (1) lets-encrypt (3) debian (11) liquid (1) magento (2) ruby (1) LETSENCRYPT (1) Fibonacci (1) wine (1) transaction (1) pendrive (1) boot (1) usb (1) prf (1) policia (2) federal (1) lucena (1) mongodb (4) paypal (1) payment (1) zend (1) vim (4) ciencia (6) js (1) nosql (1) java (1) JasperReports (1) phpjasper (1) covid19 (1) saude (1) athena (1) cinnamon (1) phpunit (2) binaural (1) mysqli (3) database (42) windows (6) vala (1) json (2) oracle (1) mariadb (4) dev (12) webdev (24) s3 (4) storage (1) kitematic (1) gnome (2) web (2) intel (3) piada (1) cron (2) dba (18) lumen (1) ffmpeg (2) android (2) aplicativo (1) fedora (2) shell (4) bash (3) script (3) lider (1) htm (1) csv (1) dropbox (1) db (3) combustivel (2) haru (1) presenter (1) gasolina (1) MeioAmbiente (1) Grunt (1) biologia (1) programming (22) performance (3) brain (1) smartphones (1) telefonia (1) privacidade (1) opensource (3) microg (1) iode (1) ssh (3) zsh (2) terminal (3) dracula (1) spaceship (1) mac (2) idiomas (1) laptop (2) developer (37) api (4) data (1) matematica (1) seguranca (2) 100DaysOfCode (9) hotfix (1) documentation (1) laravelphp (10) RabbitMQ (1) Elasticsearch (1) redis (2) Raspberry (4) Padrao de design (4) JQuery (1) angularjs (4) Dicas (40) Kubernetes (3) vscode (2) backup (1) angular (3) servers (2) pipelines (1) AppSec (1) DevSecOps (4) rust (1) RustLang (1) Mozilla (1) algoritimo (1) sqlite (1) Passport (1) jwt (4) security (2) translate (1) kube (2) iot (1) politica (2) bolsonaro (1) flow (1) podcast (1) Brasil (1) containers (3) traefik (1) networking (1) host (1) POO (2) microservices (2) bug (1) cqrs (1) arquitetura (2) Architecture (3) sail (3) militar (1) artigo (1) economia (1) forcas armadas (1) ffaa (1) autenticacao (1) autorizacao (2) authentication (4) authorization (2) NoCookies (1) wsl (4) memcached (1) macos (2) unix (2) kali-linux (1) linux-tools (5) apple (1) noticias (2) composer (1) rancher (1) k8s (1) escopos (1) orm (1) jenkins (4) github (5) gitlab (3) queue (1) Passwordless (1) sonarqube (1) phpswoole (1) laraveloctane (1) Swoole (1) Swoole (1) octane (1) Structurizr (1) Diagramas (1) c4 (1) c4-models (1) compactar (1) compression (1) messaging (1) restfull (1) eventdrive (1) services (1) http (1) Monolith (1) microservice (1) historia (1) educacao (1) cavalotroia (1) OOD (0) odd (1) chatgpt (1) openai (3) vicuna (1) llama (1) gpt (1) transformers (1) pytorch (1) tensorflow (1) akitando (1) ia (1) nvidia (1) agi (1) guard (1) multiple_authen (2) rpi (1) auth (1) auth (1) livros (2) ElonMusk (2) Oh My Zsh (1) Manjaro (1) BigLinux (2) ArchLinux (1) Migration (1) Error (1) Monitor (1) Filament (1) LaravelFilament (1) replication (1) phpfpm (1) cache (1) vpn (1) l2tp (1) zorin-os (1) optimization (1) scheduling (1) monitoring (2) linkedin (1) community (1) inteligencia-artificial (2) wsl2 (1) maps (1) API_KEY_GOOGLE_MAPS (1) repmgr (1) altadisponibilidade (1) banco (1) modelagemdedados (1) inteligenciadedados (4) governancadedados (1) bancodedados (2) Observability (1) picpay (1) ecommerce (1) Curisidades (1) Samurai (1) KubeCon (1) GitOps (1)

New Articles



Get Latest Updates by Email