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_postswithpost_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, andwp_options wp_optionsautoloaded 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:
- 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). - 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. - Orphaned postmeta removal —
DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL; - Table optimization (OPTIMIZE TABLE) — ran
OPTIMIZE TABLEonwp_posts,wp_postmeta,wp_options, andwp_woocommerce_order_items. - Autoload audit — identified all options with
autoload = 'yes'and size > 50 KB using a query againstinformation_schema, then set non-essential ones (cached remote API responses, stale plugin settings) toautoload = 'no'viawp 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_poststable 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 TABLEdelivered 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 inSHOW 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:
- Audit and reduce autoloaded options (biggest TTFB gain).
- Run
OPTIMIZE TABLEon the four largest tables (biggest I/O gain). - Delete expired transients (reduces query count and autoload size simultaneously).
- Prune revisions and set
WP_POST_REVISIONS = 3(prevents recurrence). - 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.