11 WordPress Database Mistakes That Will Crash Your Site
Most WordPress crashes start in the database. After reviewing hundreds of sites, these are the 11 database mistakes that cause the most damage — and exactly how to fix each one.

Your Database Is a Ticking Time Bomb
Most WordPress crashes are not caused by bad plugins or cheap hosting. They start in the database. A single unindexed query, a bloated options table, or a missing cleanup routine can bring a site to its knees under real traffic.
After reviewing hundreds of WordPress databases, the same 11 mistakes appear over and over. Here is what they are, why they happen, and exactly how to fix each one before it takes your site down. For more speed and caching guides, explore our performance hub.
WordPress loads every autoloaded option on every single page request. When plugins dump megabytes of serialized data into wp_options with autoload set to yes, your site loads that data whether it needs it or not.
The Wrong Way
Saving large arrays, logs, or cache data directly into wp_options without setting autoload to no.
The Right Way
Audit your autoloaded options regularly. Run this query to find the biggest offenders:
SELECT option_name, LENGTH(option_value) AS sizeAny option larger than 100KB that does not need to load on every page should be switched to autoload=no. For plugin developers, always use the fourth parameter in
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 20;add_option() or call update_option( $key, $value, false ) to disable autoloading.
WordPress stores every revision of every post by default. A site with 500 posts and 50 revisions each has 25,000 extra rows in wp_posts. That is dead weight slowing down every post query.
The Wrong Way
Leaving WP_POST_REVISIONS at its default unlimited value and never purging old revisions.
The Right Way
Limit revisions in wp-config.php:
define( 'WP_POST_REVISIONS', 5 );Then clean up existing revisions with WP-CLI:
wp post delete $(wp post list --post_type=revision --format=ids) --forceOr be selective and delete revisions older than 90 days using a direct query on wp_posts where post_type is revision and post_date is older than your cutoff.
Transients are temporary cached values stored in wp_options. When they expire, WordPress does not automatically delete them. Over months, thousands of expired transients pile up, bloating the options table.
The Wrong Way
Assuming WordPress handles transient cleanup automatically. It does not unless you have an object cache.
The Right Way
Delete expired transients periodically:
wp transient delete --expiredBetter yet, install a persistent object cache like Redis. When you use Redis, transients are stored in memory instead of the database, and Redis handles expiration natively.
Using $wpdb->query() with raw user input is the fastest way to get SQL injected. It also bypasses WordPress caching and can cause unexpected results with special characters.
The Wrong Way
$wpdb->query( "DELETE FROM wp_posts WHERE ID = " . $_GET['id'] );The Right Way
Always use $wpdb->prepare():
$wpdb->query(The prepare method escapes inputs and prevents injection. There is never a valid reason to skip it when user data touches your queries.
$wpdb->prepare( "DELETE FROM wp_posts WHERE ID = %d", absint( $_GET['id'] ) )
);
When plugins create custom tables, they often skip adding proper indexes. The table works fine with 100 rows. At 100,000 rows, every query becomes a full table scan.
The Wrong Way
Creating a table with only a primary key and no additional indexes on columns you filter or sort by.
The Right Way
Add indexes on any column used in WHERE, ORDER BY, or JOIN clauses:
ALTER TABLE wp_custom_logs ADD INDEX idx_user_date (user_id, created_at);Check for missing indexes by running EXPLAIN on slow queries. If you see “Using filesort” or “Using temporary” with large row counts, you need an index.
MyISAM uses table-level locking. When one query writes to a table, every other query on that table waits. Under any real traffic, this creates a queue that brings your site down.
The Wrong Way
Running older tables on MyISAM because “it has always worked.”
The Right Way
Convert all tables to InnoDB:
ALTER TABLE wp_posts ENGINE=InnoDB;InnoDB supports row-level locking, crash recovery, and foreign keys. There is no modern reason to use MyISAM for WordPress. Check all your tables with
ALTER TABLE wp_postmeta ENGINE=InnoDB;
ALTER TABLE wp_options ENGINE=InnoDB;SHOW TABLE STATUS; and convert any that are still MyISAM.
wp_postmeta is the most abused table in WordPress. Plugins store everything there because it is easy. A WooCommerce store with 10,000 orders can have millions of postmeta rows.
The Wrong Way
Querying postmeta with multiple JOINs on unindexed meta_key and meta_value columns.
The Right Way
Add a composite index on meta_key and post_id:
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_post (meta_key(191), post_id);For high-volume sites, consider moving frequently queried meta into a custom table with proper columns instead of the EAV (Entity-Attribute-Value) pattern that postmeta uses.
When you delete a post, WordPress deletes the post row but sometimes leaves its postmeta, term relationships, and comments behind. Plugins that store custom post types are especially bad at cleanup.
The Wrong Way
Deleting posts without checking if their metadata gets cleaned up.
The Right Way
Periodically clean orphaned postmeta:
DELETE pm FROM wp_postmeta pmDo the same for wp_term_relationships and wp_comments. On a site with years of plugin installs and removals, this can reclaim thousands of rows.
LEFT JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.ID IS NULL;
Counting all posts, calculating statistics, or running complex JOINs on every page load is a performance killer. These queries hit the database when the result rarely changes.
The Wrong Way
Putting a complex aggregate query inside a shortcode or widget that runs on every page view.
The Right Way
Cache expensive query results using transients or object cache:
$stats = get_transient( 'site_stats' );Invalidate the cache only when the underlying data changes, not on every page load.
if ( false === $stats ) {
$stats = expensive_stats_query();
set_transient( 'site_stats', $stats, HOUR_IN_SECONDS );
}
The InnoDB buffer pool is where MySQL keeps table and index data in memory. The default is often 128MB, which is tiny for a WordPress site with any traffic.
The Wrong Way
Running MySQL with default settings on a server with 4GB+ RAM.
The Right Way
Set innodb_buffer_pool_size to 50-70% of available RAM for a dedicated database server:
[mysqld]Monitor buffer pool hit rate with
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256MSHOW STATUS LIKE 'Innodb_buffer_pool_read%';, you want a hit rate above 99%. If reads from disk are high, increase the buffer pool.
Tables fragment over time. Deleted rows leave gaps. Indexes get stale. Without regular maintenance, performance degrades slowly until one day the site crawls.
The Wrong Way
Never running OPTIMIZE TABLE or checking table health.
The Right Way
Set up a monthly maintenance routine:
wp db optimizeOr run it per-table for large databases:
OPTIMIZE TABLE wp_posts;Combine this with monitoring. Set up slow query logging (
OPTIMIZE TABLE wp_postmeta;
OPTIMIZE TABLE wp_options;slow_query_log = 1 and long_query_time = 1) to catch degrading queries before users notice.
Run through this monthly:
- Audit autoloaded options, nothing over 100KB unless essential
- Purge post revisions beyond your limit
- Delete expired transients
- Clean orphaned postmeta, term relationships, and comments
- Check all tables are InnoDB
- Verify indexes on custom tables
- Review slow query log for new offenders
- Run OPTIMIZE TABLE on large tables
- Confirm buffer pool hit rate is above 99%
A healthy database is the single biggest performance lever for WordPress. Fix these 11 mistakes and your site will handle traffic spikes that would crash most WordPress installations. If you encounter issues along the way, our fixing WordPress errors guides can help you troubleshoot.
Plugins we build and run
We maintain these ourselves, which is why we can support them on your site.
- BuddyNextThe self-hosted community engine for WordPress
- BuddyXFree community theme, Pro when you outgrow it
- ReignPremium community theme. Add pieces, build anything
- JetonomyCommunity forum and Q&A for WordPress
- EventonomyEvents and RSVPs for WordPress communities
- LearnomyCourses and learning communities on WordPress
- ListoraDirectories and listings your members can browse
- SnipShareCode sharing built for developer communities
- WB Ad ManagerAd placements that turn traffic into revenue
- WP Career BoardA job board your community actually uses
- WP Sell ServicesSell services with offers, orders, and payouts
- MediaVersePhotos, video, and media albums for members
- WB GamificationPoints, badges, and rewards that keep members active
- Product RoadmapPublic roadmaps with voting your users trust


