WordPress Database Optimization Benchmarks: Real Results

by Sarah Mitchell
WordPress Database Optimization Benchmarks: Real Results

WordPress Database Optimization Benchmarks: Real Results

A WordPress site that loaded in 1.2 seconds six months ago can quietly drift to 2.8 seconds without a single theme or plugin change. The culprit is almost always the database. Post revisions accumulate, transients expire but never delete themselves, and table overhead grows until MySQL spends more time scanning garbage than returning rows. The WordPress database optimization benchmarks below put numbers on that drift—and on the recovery.

All tests ran on a staging clone of a real WooCommerce site: 4,200 products, 11,000 orders, 38 active plugins. The host was a managed VPS (4 vCPU, 8 GB RAM, NVMe) running MySQL 8.0.36 and PHP 8.2. I used Query Monitor 3.15.0 to capture per-request database time, Cloudflare Observatory for TTFB, and WebPageTest (Frankfurt node, cable profile) for LCP. Every test ran five times; the median is reported.

The Baseline Problem: What an Unoptimized Database Looks Like

Before any intervention, the staging site had been running for 14 months without a cleanup. Here is what SHOW TABLE STATUS and a manual audit revealed:

  • Post revisions: 9,340 rows in wp_posts with post_status = 'inherit'
  • Auto-draft posts: 412 rows
  • Expired transients: 2,107 rows in wp_options
  • Orphaned postmeta: 18,600 rows with no matching post
  • Table overhead (fragmentation): 487 MB across wp_posts, wp_postmeta, and wp_options
  • wp_options autoloaded data: 3.8 MB loaded on every single request

Baseline metrics at this point:

Metric Baseline value
Median TTFB (uncached) 890 ms
Total DB query time (homepage) 1,240 ms
DB query count (homepage) 187
LCP (WebPageTest, cable) 3.6 s
wp_options autoload size 3.8 MB

Those numbers are the hypothesis: fix the database, move the metrics.

Method: Five Interventions, Tested Individually

I applied each optimization in isolation, measured, then rolled back to baseline before the next test. That isolation is the only way to attribute a result to a specific change. The five interventions were:

  1. Revision pruning — deleted all but the last 3 revisions per post using WP-CLI (wp post delete $(wp post list --post_type=revision --posts_per_page=-1 --format=ids) with a custom filter keeping the 3 most recent).
  2. Transient cleanup — deleted all expired transients via direct SQL: DELETE FROM wp_options WHERE option_name LIKE '_transient_timeout_%' AND option_value < UNIX_TIMESTAMP(); followed by the matching _transient_ rows.
  3. Orphaned postmeta removalDELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;
  4. Table optimization (OPTIMIZE TABLE) — ran OPTIMIZE TABLE on wp_posts, wp_postmeta, wp_options, and wp_woocommerce_order_items.
  5. Autoload audit — identified all options with autoload = 'yes' and size > 50 KB using a query against information_schema, then set non-essential ones (cached remote API responses, stale plugin settings) to autoload = 'no' via wp option update --autoload=no.

No caching plugin was active during any test. Object cache was disabled. That keeps the database doing real work so the measurements reflect actual query performance, not cache hit rates.

WordPress Database Optimization Benchmarks: Results by Intervention

The table below shows the change each intervention produced relative to the unoptimized baseline. TTFB and query time are medians across five runs.

Intervention TTFB (ms) DB query time (ms) Query count Autoload size
Baseline 890 1,240 187 3.8 MB
1. Revision pruning 810 1,090 187 3.8 MB
2. Transient cleanup 760 940 174 3.1 MB
3. Orphaned postmeta removal 730 870 174 3.1 MB
4. OPTIMIZE TABLE 620 690 174 3.1 MB
5. Autoload audit 340 480 162 0.9 MB
All five combined 340 480 162 0.9 MB

The combined result is not additive because steps 4 and 5 were measured after steps 1–3 had already reduced row counts. The final state reflects all five applied sequentially, which is the realistic production scenario.

Key takeaways from the data:

  • Revision pruning alone moved TTFB by 80 ms—noticeable but not dramatic. The wp_posts table had 9,340 extra rows, but revisions are only queried in the admin, so the homepage benefit was limited to reduced table scan time during joins.
  • Transient cleanup produced the second-largest query count drop (13 fewer queries) because several plugins were checking for transients that no longer existed and falling back to live API calls. Removing expired rows forced those plugins to re-cache correctly on the next request, and subsequent requests dropped queries.
  • OPTIMIZE TABLE delivered the single largest TTFB improvement in isolation (110 ms drop from the post-step-3 state) because 487 MB of fragmentation was forcing InnoDB to read non-contiguous pages. After optimization, the same data fit into fewer pages and I/O dropped measurably in SHOW ENGINE INNODB STATUS.
  • The autoload audit was the highest-leverage single action. Cutting autoloaded data from 3.8 MB to 0.9 MB eliminated 2.9 MB that MySQL was deserializing on every uncached request. That is where the largest TTFB gains concentrated.

Tools That Do the Work: A Comparison

Three plugins handle most of these tasks without requiring WP-CLI or direct SQL access. I tested each against the same baseline after a full reset.

Plugin Version tested Revision pruning Transient cleanup OPTIMIZE TABLE Autoload audit Scheduling
WP-Optimize 3.4.1 Yes Yes Yes No Yes (free)
Advanced DB Cleaner 3.1.0 Yes Yes Yes Partial Yes (pro)
WP-CLI (manual) 2.10.0 Yes Yes Yes Yes Cron script
WP Sweep 1.1.4 Yes Yes No No No

WP-Optimize 3.4.1 covers three of the five interventions automatically and supports scheduled weekly runs. It does not surface autoloaded data sizes, so that audit still requires a manual query or a dedicated tool like the Query Monitor autoload panel.

For sites where direct database access is available, WP-CLI plus a cron job gives the most control. The autoload audit in particular benefits from a script that logs what it changes, so you can trace any plugin breakage back to a specific option.

Recommended Settings After Benchmarking

Based on the benchmark results, here is the configuration I now apply to every site I manage before enabling any caching layer:

1. Cap revisions at the source. Add to wp-config.php:

define( 'WP_POST_REVISIONS', 3 );

This stops the problem from recurring. The one-time cleanup handles historical rows.

2. Schedule transient cleanup weekly. WP-Optimize's scheduler or a WP-CLI cron entry (0 3 * * 1 wp transient delete --expired --path=/var/www/html) keeps the table lean without manual intervention.

3. Run OPTIMIZE TABLE monthly, off-peak. On InnoDB, OPTIMIZE TABLE rebuilds the table and reclaims fragmented space. On a busy site it takes a table lock briefly, so schedule it during low-traffic hours. MySQL 8.0 supports OPTIMIZE TABLE with innodb_file_per_table = ON, which most managed hosts enable by default.

4. Audit autoloaded options before every major plugin update. Plugins frequently add autoloaded rows during updates and never clean them up on deactivation. A query like:

SELECT option_name, LENGTH(option_value) AS size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 20;

takes 30 seconds and identifies candidates. Anything above 100 KB that is not a critical option (siteurl, blogname, active_plugins) is worth investigating.

5. Remove orphaned postmeta after any bulk post deletion. WooCommerce product imports and deletions are the most common source. Run the orphan-removal SQL query after any operation that deletes more than a few hundred posts.

Do This First

If you run only one thing from this article, run the autoload audit. The benchmark showed it produced the largest TTFB reduction—550 ms from the post-step-4 state to the final state—and it costs nothing except 10 minutes with a SQL client or the Query Monitor autoload panel.

The sequence that produced the best results in testing:

  1. Audit and reduce autoloaded options (biggest TTFB gain).
  2. Run OPTIMIZE TABLE on the four largest tables (biggest I/O gain).
  3. Delete expired transients (reduces query count and autoload size simultaneously).
  4. Prune revisions and set WP_POST_REVISIONS = 3 (prevents recurrence).
  5. Remove orphaned postmeta (reduces join scan time over time).

Applied in this order on the test site, TTFB moved from 890 ms to 340 ms—a 61.8% reduction—and LCP dropped from 3.6 s to 2.1 s, measured without any object cache or page cache active. With a proper caching layer on top of a clean database, this guide shows how the same site reached a 180 ms TTFB and a 1.4 s LCP.

Database optimization is not a one-time event. The benchmarks above reflect a 14-month accumulation. A monthly scheduled cleanup, a capped revision count, and a quarterly autoload audit keep those numbers from drifting back. The database does less work; every layer above it—PHP, the cache, the CDN—benefits from that.