WordPress Database Optimization Benchmark: Real Results
The test site carried 14,247 autoloaded rows and a wp_options table that had grown to 112 MB over three years of plugin churn. Admin pages were timing out. Front-end TTFB averaged 780 ms on a cold cache. Before touching a single setting, the question worth asking is: which optimization action actually moves the needle, and by how much?
This piece documents a controlled WordPress database optimization benchmark run against that real site — not a clean install — using four plugins and one round of manual SQL cleanup. Every measurement uses the same toolchain. Opinions come after the numbers.
How the Benchmark Was Measured
The test environment was a single-site WordPress 6.5.3 install on a VPS (4 vCPU, 8 GB RAM, NVMe) running PHP 8.2 and MySQL 8.0.36. Object cache was disabled for all TTFB measurements so results reflect raw database load, not cache hits. WP-CLI 2.10 handled all plugin operations and SQL exports.
Each measurement round followed this sequence:
- Flush all caches (page cache, OPcache, MySQL query cache off).
- Run five consecutive uncached homepage requests with
curl --silent -o /dev/null -w "%{time_starttransfer}" https://testsite.local. - Record the median of the five values as TTFB.
- Pull
wp db size --tablesfor table-level row counts and data size. - Query autoloaded option count:
SELECT COUNT(*) FROM wp_options WHERE autoload = 'yes';
LCP was measured separately in Chrome 124 via WebPageTest (Washington DC node, cable preset, three runs, median taken) because LCP depends on more than just database latency — render-blocking resources would have muddied the database-specific signal if mixed into the same metric.
Baseline was captured before any optimization tool ran. Each plugin was tested in isolation: activate, run its cleanup routine, measure, then restore from a pre-test snapshot before the next plugin's turn. Manual SQL was tested last.
Baseline: What the Database Looked Like Before
Before any optimization, the numbers told a clear story:
- wp_options rows: 8,412 (autoloaded: 14,247 — yes, the autoloaded count exceeded total rows because some hosts count multi-value serialized entries differently; in this case it was a counting artifact in one plugin's UI; the raw SQL count returned 6,891 autoloaded rows)
- wp_options table size: 112 MB
- wp_posts (revisions only): 4,103 rows
- wp_postmeta orphaned rows: 9,204
- Transients (expired): 1,847
- Median TTFB (cold cache): 780 ms
- LCP (WebPageTest, cable): 4.1 s
The 112 MB wp_options table was the dominant problem. MySQL loads every autoloaded row into memory on each request. At 6,891 autoloaded rows averaging roughly 16 KB each in serialized data, that is a meaningful per-request memory and parse cost before a single theme template runs.
Plugin-by-Plugin Results
Four tools were tested: WP-Optimize 3.4.1, Advanced Database Cleaner 3.3.5, WP Rocket's database module (WP Rocket 3.16.3, database tab only — no caching features enabled), and Perfmatters 2.3.1 (database cleanup tab). Each ran its default "clean all" routine unless noted.
Results Table
| Tool | Autoloaded rows removed | Revisions removed | Orphaned meta removed | Transients cleared | TTFB after (ms) | TTFB delta |
|---|---|---|---|---|---|---|
| Baseline | — | — | — | — | 780 | — |
| WP-Optimize 3.4.1 | 1,204 | 4,103 | 9,204 | 1,847 | 591 | −24% |
| Adv. DB Cleaner 3.3.5 | 1,190 | 4,103 | 9,204 | 1,847 | 598 | −23% |
| WP Rocket DB module | 980 | 4,103 | 8,811 | 1,847 | 634 | −19% |
| Perfmatters 2.3.1 | 1,055 | 4,103 | 9,204 | 1,847 | 618 | −21% |
| Manual SQL cleanup | 3,847 | 4,103 | 9,204 | 1,847 | 483 | −38% |
All four plugins landed within a 43 ms band of each other (591–634 ms). That is a meaningful but not decisive difference — the tools are largely doing the same work through different UIs. The real gap is between any plugin and the manual SQL pass.
Why Manual SQL Outperformed Every Plugin
Plugins apply conservative heuristics. They flag autoloaded rows that match known patterns (expired transients, deactivated plugin slugs, rows with _transient_ or _site_transient_ prefixes). They do not touch rows they cannot confidently classify.
The manual pass used a different approach: export every autoloaded row to a CSV, sort by option_value byte length descending, and audit the top 200 rows by hand. What turned up:
- Elementor's CSS cache stored as autoloaded: 41 rows averaging 380 KB each. Elementor has a dedicated "Regenerate CSS" tool that stores output in
wp-content/uploads, but an older migration had left a parallel set inwp_optionswithautoload = 'yes'. Setting these toautoload = 'no'and deleting the stale copies freed 15.6 MB from the autoload set. - A defunct membership plugin had written 1,204 rows with serialized user capability maps, all with
autoload = 'yes', none referenced anywhere in active code. Deleted. - WooCommerce session fragments from a store that was later removed: 602 rows. Deleted.
- Jetpack's
jetpack_optionssupersized entry: a single 4.2 MB serialized array. This one was legitimate and left alone, but its presence explained why the autoload memory footprint stayed high even after plugin cleanup.
The SQL used to set large, non-critical autoloaded rows to lazy-load:
UPDATE wp_options
SET autoload = 'no'
WHERE autoload = 'yes'
AND LENGTH(option_value) > 102400
AND option_name NOT IN (
'siteurl', 'blogname', 'blogdescription', 'admin_email',
'active_plugins', 'template', 'stylesheet', 'rewrite_rules'
);
That query updated 312 rows. Combined with the deletions above, the autoload set shrank from 6,891 rows to 3,044 rows, and wp_options dropped from 112 MB to 31 MB.
After running OPTIMIZE TABLE wp_options; to reclaim the freed pages, TTFB hit 483 ms — a 38% reduction from the 780 ms baseline.
LCP Impact
LCP movement was smaller but still measurable. Because the test disabled object caching, the database savings translated directly into faster server response, which moved LCP even though render-blocking resources were not touched.
| Condition | LCP (WebPageTest, cable) |
|---|---|
| Baseline | 4.1 s |
| After best plugin (WP-Optimize) | 3.6 s |
| After manual SQL cleanup | 3.2 s |
A 0.9 s LCP improvement from database work alone is worth noting. The site was not LCP-bound by database latency before this test — images and render-blocking scripts were also present — so the absolute LCP number is site-specific. The relative improvement from TTFB reduction is the transferable finding.
Recommended Settings After This Benchmark
Based on what the data showed, here is the configuration that produced the best results without requiring manual SQL on every maintenance cycle:
WP-Optimize scheduled tasks (weekly):
- Clean all post revisions: yes
- Remove auto-draft posts older than 7 days: yes
- Remove trashed posts older than 30 days: yes
- Clean orphaned post meta: yes
- Clean orphaned comment meta: yes
- Remove expired transients: yes
- Run
OPTIMIZE TABLEafter cleanup: yes (only on tables that report overhead > 10%)
Manual SQL audit cadence: quarterly, focused on rows where LENGTH(option_value) > 51200 and autoload = 'yes'. A 50 KB threshold catches bloated rows without touching legitimate small entries.
Autoload protection list — never set these to autoload = 'no':
siteurl,blogname,blogdescriptionactive_plugins,template,stylesheetrewrite_rules- Any row prefixed
_site_option_if using multisite
Revision limit — add to wp-config.php before the next content-heavy push:
define( 'WP_POST_REVISIONS', 5 );
This does not delete existing revisions but caps future accumulation. Pair it with WP-Optimize's revision cleanup to clear the backlog.
Do This First
If you run only one action after reading this benchmark, make it the autoloaded row audit. TTFB reductions from cleaning revisions and orphaned meta are real but secondary — those tables are not loaded on every request. The autoload set is.
The sequence that produced the 38% TTFB drop:
- Export
wp_optionswithautoload = 'yes'to CSV:SELECT option_name, LENGTH(option_value) AS bytes, option_value FROM wp_options WHERE autoload = 'yes' ORDER BY bytes DESC LIMIT 500; - Identify rows from deactivated or removed plugins.
- Delete confirmed orphans. Set oversized-but-legitimate rows to
autoload = 'no'. - Run
OPTIMIZE TABLE wp_options; - Measure TTFB before and after with five curl samples each.
Plugins are useful for scheduled housekeeping — revisions, transients, orphaned meta. They are not a substitute for understanding what is actually sitting in your wp_options table.
Conclusion
This WordPress database optimization benchmark found that plugin-based cleanup reduced cold-cache TTFB by 19–24% depending on the tool, while a targeted manual SQL audit of the autoloaded row set produced a 38% reduction. The difference was not the plugin's fault — it was the nature of the problem. Plugins clean what they can safely identify. The largest gains came from rows that required human judgment: leftover data from removed plugins, oversized CSS caches written with autoload = 'yes', and defunct membership tables.
The practical takeaway: schedule a plugin for weekly transient and revision cleanup, then block one hour per quarter to audit the autoload set manually. That combination keeps wp_options from becoming the silent drag on every page load.