How to use Query Monitor to detect slow queries in WordPress

Contents

Introduction

Query Monitor is a powerful WordPress developer plugin that helps you find slow database queries, expensive hooks, excessive HTTP requests, and many other performance problems. This article explains, step by step, how to use Query Monitor to detect slow queries, interpret its output, reproduce and verify problems, and apply practical fixes. It includes configuration snippets, debugging commands, SQL examples and common fixes for real-world WordPress performance issues.

Installation and basic setup

There are three simple ways to install Query Monitor: via the admin Plugins screen, via Composer/Composer Packagist, or via WP-CLI. Once installed, Query Monitor appears in the admin toolbar for logged-in users with the necessary capabilities (typically administrators).

Install via Plugins screen

  • Go to Plugins gt Add New, search for Query Monitor, then install and activate.

Install via WP-CLI

wp plugin install query-monitor --activate

Enable query collection for non-admins or to persist more detail

Query Monitor reads the WordPress constant SAVEQUERIES to capture query backtraces and execution times. For production or to capture more data you may toggle this in wp-config.php. Add the following near the top of wp-config.php (before ABSPATH or before WP_DEBUG checks).

/ Enable query saving so Query Monitor can show backtraces and times /
if ( ! defined( SAVEQUERIES ) ) {
    define( SAVEQUERIES, true )
}
/ (Optional) Enable WP debug display/logging /
if ( ! defined( WP_DEBUG ) ) {
    define( WP_DEBUG, true )
}

Note: SAVEQUERIES adds overhead. Enable it temporarily while diagnosing performance problems, or only enable for specific environments (staging).

Understanding the Query Monitor interface

After activation and while logged in, the Query Monitor menu appears in the WordPress admin toolbar. Click it to open its panels. The core panels relevant to slow queries include:

Panel What it shows
Queries All database queries executed for the current request, sorted and filterable by component (core, plugin, theme), caller, and time. It shows execution time and backtrace for each query.
Queries by caller Groups queries by the PHP function/file that issued them — essential for finding which plugin or theme code triggers expensive queries.
Duplicate queries Identifies queries that run repeatedly with identical SQL — often a sign of caching or code issues (N 1 problems).
HTTP API Shows external HTTP requests (not DB queries) that might slow page loads.
PHP Errors / Hooks and Actions Shows slow hooks and PHP errors that can correlate with slow queries or extra work.

Step-by-step: Using Query Monitor to detect slow queries

  1. Reproduce the slow page or action

    Make the frontend or admin request that is slow. Keep a browser tab open as you will inspect Query Monitor for that specific request.

  2. Open Query Monitor from the admin toolbar

    Click the Query Monitor menu and choose the Queries panel or click the small total queries label in the admin bar to jump straight to the queries listing.

  3. Sort by execution time

    In the Queries panel click the header for time (or use the Slowest queries filter) to show the slowest queries first. Query Monitor lists per-query execution time and total time for repeated queries.

  4. Check the Queries by caller table

    Switch to Queries by caller to see which plugin/theme function or core file issued the query — this points to the source for optimization (for example, a theme template running a subquery in a loop).

  5. Look for duplicates and N 1 patterns

    Use the Duplicate queries and Group identical queries options. Multiple identical queries or many similar queries with different parameters often indicate N 1 problems.

  6. Use the backtrace and caller link

    Click the backtrace/caller entry to see the exact function and file location. That allows you to open the source file and implement fixes or caching.

  7. Click Explain or copy the SQL

    For SELECT statements Query Monitor offers an Explain link (if enabled and if the plugin can run EXPLAIN). Use that to inspect whether a table scan is happening, or copy the SQL for manual EXPLAIN in your DB client.

  8. Verify improvements after changes

    After altering code or adding an index, reproduce the request and compare Query Monitor results before and after to confirm the issue is fixed.

Example: Enable EXPLAIN and analyze a slow SELECT

A common troubleshooting workflow is: locate a slow SELECT in Query Monitor, copy the SQL, run EXPLAIN in MySQL, and then add an index or rewrite the query. Below is an example of copying a slow query and running EXPLAIN via the mysql client.

EXPLAIN SELECT p.ID, p.post_title
FROM wp_posts p
JOIN wp_postmeta pm ON ( p.ID = pm.post_id )
WHERE pm.meta_key = my_meta_key AND pm.meta_value = value
AND p.post_status = publish

If EXPLAIN shows Using where and a full table scan on wp_postmeta, consider adding an index on meta_key and/or on (post_id,meta_key) depending on the query pattern. Example index:

ALTER TABLE wp_postmeta
ADD KEY meta_key_post_id (meta_key(191), post_id)

Notes: MySQL index prefix length depends on column charset and engine adjust the prefix length and index columns to match your query patterns. Indexing meta_value is usually not feasible (large, variable-length), so design queries to filter by meta_key (indexed) or move high-traffic meta to a custom table.

Common slow-query patterns and how to fix them

N 1 queries from loops

Symptom: Many similar SELECT queries differing only by ID, triggered inside a loop (for example, calling get_post or get_post_meta inside a loop for each item).

Fixes:

  • Use a single query that fetches all needed data (use WP_Query with proper joins or a single SQL query).
  • Use functions that prime caches: use update_post_meta_cache(), update_post_caches(), or get_posts() with a single query to load all posts then process them.
// Bad: N 1 pattern - calling get_post_meta inside loop
foreach ( post_ids as id ) {
    value = get_post_meta( id, color, true ) // runs a query per ID
}

// Better: prime the meta cache and then read from cache
update_postmeta_cache( post_ids ) // or update_post_meta_cache() depending on WP version
foreach ( post_ids as id ) {
    value = get_post_meta( id, color, true ) // no query per ID after priming
}

Expensive meta queries (meta_query)

Meta queries (meta_query) are frequent causes of slowness because wp_postmeta can be large and not always well indexed for the query. Query Monitor will show these queries and their backtraces.

Fixes:

  • Add targeted indexes (meta_key or meta_key post_id) when your queries consistently filter by meta_key.
  • Where possible store frequently queried values in a custom, indexed table or use taxonomies instead of meta for large data.
  • Use transients or the object cache for expensive repeated lookups.

Taxonomy and term queries

Complex taxonomy queries (especially JOINs across wp_term_relationships and wp_posts) can be slow when combining many terms or when taxonomy structure is large.

Fixes:

  • Use WP_Query with optimized args (limit fields, avoid complex nested tax_query when possible).
  • Consider caching term query results with transient or object cache.
  • Review and add indexes when necessary (rare for default schema but sometimes helpful for custom setups).

Duplicate queries / repeated identical queries

Query Monitor highlights duplicate SQL. Identical repeated queries are usually a wasted opportunity to cache or prime results.

Fixes:

  • Identify the caller and implement transient or object-cache caching for the result.
  • Reduce calls by aggregating requests into a single call and then reusing the in-memory result during the request lifecycle.

Advanced debugging techniques

Search codebase for the caller

Once Query Monitor shows the caller (file line), locate it in your codebase with grep or your IDE. Example using grep:

# Search for a function name or pattern returned by Query Monitor
grep -RIn function_or_file_name wp-content/themes wp-content/plugins

Use MySQL slow query log for deeper analysis

If database-wide monitoring is required (requests across many pages), enable MySQLs slow query log and analyze with pt-query-digest or similar tools. Example MySQL commands to temporarily enable the log (requires DB admin access):

SET GLOBAL slow_query_log = ON
SET GLOBAL long_query_time = 0.5 -- log queries longer than 0.5s
-- The log file location depends on server config check slow_query_log_file
SHOW VARIABLES LIKE slow_query_log_file

Then use pt-query-digest to aggregate and find top offenders:

pt-query-digest /path/to/slow-query.log

Profile PHP execution to correlate heavy PHP with slow SQL

If Query Monitor shows that a particular function triggers many or long queries, use Xdebug or a profiler to sample the PHP execution and find hotspots. Query Monitors backtraces give a starting point full profiling shows CPU and memory distribution.

Practical example: Diagnose and fix a slow product archive

  1. Load the archive page and note it is slow.
  2. Open Query Monitor gt Queries and sort by time. Identify a long-running SELECT joining wp_posts and wp_postmeta filtering by a meta_key price and meta_value range.
  3. Click the caller to locate the plugin or theme template that performs this query — its from the theme function archive_loop().
  4. Copy the SQL and run EXPLAIN. It shows full table scans on wp_postmeta.
  5. Options to fix:
    • Add a composite index on (meta_key, post_id) if queries always filter by meta_key and then post_id.
    • Move product pricing into a custom table with a proper schema and indexes for range queries.
    • Cache expensive results with transients and invalidate on product updates.
  6. After applying the fix (for example, custom table), re-run the archive page and verify in Query Monitor that the slow queries are gone or much faster.

Troubleshooting tips and best practices

  • Enable SAVEQUERIES only when diagnosing it increases memory and CPU use.
  • Use Query Monitor on staging or developer environments to avoid exposing backtraces on production.
  • Prioritize changes that remove repeated queries (duplicates / N 1) before micro-optimizing single fast queries.
  • Use object cache (Redis / Memcached) for repeated heavy lookups Query Monitor will show whether a query result is served from cache or run fresh.
  • Avoid SELECT in custom queries request only the columns you need to reduce IO and improve caching behavior.
  • Always test performance before and after changes and keep backups before altering DB schema or adding indexes.

When Query Monitor is not sufficient

Query Monitor is request-scoped and excellent for finding problems in specific pages or admin actions. For long-term or system-level analysis, use:

  • MySQL slow query log and pt-query-digest to aggregate heavy queries across traffic.
  • Application performance monitoring (APM) tools such as New Relic or Blackfire for continuous profiling and historical trends.
  • Load testing tools to reproduce concurrency issues that Query Monitor (single request) cannot reveal.

Appendix: Useful snippets and commands

Enable SAVEQUERIES in wp-config.php

/ Enable query saving for debugging (temporary) /
if ( ! defined( SAVEQUERIES ) ) {
    define( SAVEQUERIES, true )
}

Prime post meta cache for many IDs

// Prime meta cache for an array of post IDs to avoid N 1
update_postmeta_cache( post_ids ) // function name varies by WP version
foreach ( post_ids as id ) {
    meta = get_post_meta( id, my_key, true )
}

Example ALTER TABLE to add index to wp_postmeta

-- Add an index on meta_key and post_id (tune prefix length to your charset)
ALTER TABLE wp_postmeta
ADD KEY meta_key_post_id (meta_key(191), post_id)

Search for caller in codebase

# Replace function_or_file_name with the caller shown in Query Monitor
grep -RIn function_or_file_name wp-content/themes wp-content/plugins

Conclusion

Query Monitor gives precise, actionable insight into which SQL queries are slow, which plugin or theme code triggers them, and whether queries are duplicated or cached. Use it to reproduce a slow request, identify the slowest queries, inspect callers and backtraces, run EXPLAIN, and then apply targeted fixes: add indexes, prime caches, consolidate queries, or move frequently queried data to a better schema or cache. Combine Query Monitor with MySQL slow logs and profiling tools for complete coverage of both request-level and system-level performance issues.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *