The MySQL slow query log is the single most useful data source for fixing a slow WordPress backend, and the single most often misread. Engineers open the file, see hundreds of queries each marked "slow," panic, and end up either fixing the wrong thing or doing nothing because there is too much. We have read a lot of slow logs in BoltAudit audits. This is the order that works.

What the log actually contains

The slow log records any query that exceeds your configured long_query_time threshold. Default is 10 seconds, which is useless — every query that hits that threshold is an obvious problem you would already know about. The threshold to use:

  • Production: 0.5 seconds. Captures queries that hurt user experience without filling the disk.
  • Diagnostic: 0.1 seconds. Run for one hour during a representative load, then revert.

To enable on a managed host, you usually edit my.cnf (or the equivalent in your hosting panel):

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.1
log_queries_not_using_indexes = 1

That last line is the one most people forget — and it is the most useful. It logs any query that performed a full table scan even if the query itself was fast, because a fast scan today becomes a slow scan when the table grows.

What a slow log entry looks like

# Time: 2026-05-05T09:14:22Z
# User@Host: wp_user[wp_user] @ localhost []
# Query_time: 0.342  Lock_time: 0.000  Rows_sent: 1  Rows_examined: 89412
SET timestamp=1715080462;
SELECT post_id, meta_value FROM wp_postmeta
WHERE meta_key = 'special_offer_end_date'
ORDER BY meta_value DESC LIMIT 1;

The four numbers that matter, in order:

  1. Query_time — how long it took. The headline number.
  2. Rows_examined vs Rows_sent — the ratio. 89412 examined to send 1 row is a textbook missing-index problem.
  3. Lock_time — time spent waiting for a table lock. Anything over 100ms here means concurrency, not query performance, is your real issue.
  4. The query itself — what work was done.

The reading order

This is the order we read a slow log in. It catches the high-impact problems first.

Step 1: Group by query template, not by individual query

A WordPress slow log can have 2000 entries that are all variations of three queries with different IDs. Looking at them individually wastes hours. Use pt-query-digest from Percona Toolkit:

pt-query-digest /var/log/mysql/slow.log

That groups by query template and shows you the top-N templates by total time spent. The one consuming 60% of total slow-log time is your top target. Often it is something nobody would have spotted reading raw entries.

Step 2: Look at total time first, not per-query time

The most expensive query is the one that runs 5000 times at 80ms each — total 400 seconds — not the one that runs once at 2 seconds. The 2-second query is annoying. The 5000-times-80ms query is your bottleneck.

pt-query-digest sorts by total time by default. Use that order.

Step 3: Categorize the top 5 templates

Each query in your top 5 will fall into one of these patterns:

Pattern A: Missing index

  • Symptom: high Rows_examined-to-Rows_sent ratio, query against a column without an index.
  • Common offender: wp_postmeta queried on meta_key and meta_value. WordPress indexes meta_key but NOT meta_value, so any query that filters by value is a full scan.
  • Fix: add an index, or refactor to query by post_id first.

Pattern B: N+1

  • Symptom: same query template appears thousands of times in a short window, each one fetching a single row.
  • Common offender: themes/plugins that do get_post_meta($id, $key) inside a loop instead of a single batch query.
  • Fix: code change. Refactor the loop to one batch query.

Pattern C: Autoload

  • Symptom: SELECT option_value FROM wp_options WHERE option_name = '...' running 50–200 times per request, often from inside loops.
  • Fix: not a database problem. Cache the result in a static variable or, better, use the WordPress object cache.

Pattern D: Stale transient cleanup

  • Symptom: DELETE FROM wp_options WHERE option_name LIKE '_transient_%' running for several seconds.
  • Common cause: a plugin with millions of expired transients running cleanup synchronously.
  • Fix: clear transients via WP-CLI, then debug why the plugin is creating them in the first place.

Pattern E: WooCommerce sessions / orders

  • Symptom: heavy queries against wp_woocommerce_sessions or wp_postmeta for order objects, often during cart updates.
  • Fix: HPOS migration if not already done, more aggressive session cleanup.

Step 4: Find the WordPress code that owns the query

Knowing the query is half the job. Knowing which plugin owns it is the other half. Three approaches:

Use Query Monitor in development. It shows you the call stack for every query. Match the call stack to a plugin or theme directory.

Add a comment to the query. Some plugins add / myplugin: order_lookup / to their queries, making them findable in the slow log directly. If the offending query has no such comment, you have to use EXPLAIN and a tracer.

Use New Relic, Tideways, or a similar APM. These tag every query with the WordPress hook and plugin that triggered it, removing the guesswork. Not free, but worth it on a busy production site.

Step 5: Fix the right thing

Most slow query log fixes are not index additions. They are:

  1. Code changes that batch N+1 lookups into a single query.
  2. Caching layer additions (object cache, transient cache, application cache).
  3. Plugin replacements when a plugin's queries are unfixable.
  4. Indexes added only when none of the above apply.

Adding an index without understanding why a query is slow often just delays the problem. A bad query against a 100k-row table with a new index might take 50ms; the same query against a 10M-row table will be slow regardless.

What never to do

  • Don't enable the general query log on production. It logs every query and will fill your disk in hours. Only the slow log is safe long-term.
  • Don't optimize a query that has acceptable Rows_examined. If the row count is reasonable and the query is still slow, the issue is server load, not the query itself.
  • Don't add indexes without measuring. Each index makes writes slower. On a write-heavy table like wp_postmeta, a wrong index costs more than the read it speeds up.

What BoltAudit does

The backend layer of every BoltAudit audit reads your site's slow query log (when the host exposes it) and applies the categorization above automatically. You get the top 5 query templates, the pattern they match, the WordPress hook that triggered them, and the recommended fix — without you having to read the log line-by-line.

Run a free audit — if your slow log has been waiting for someone to read it, this is the easiest way to make sense of it.

Run BoltAudit on your site

Free plugin · 1 site · 3 audits per month · no credit card.

See plans →