Ever had your WordPress site become painfully slow as it grew? I certainly have.
My client’s online store was humming along nicely until Black Friday hit. Suddenly, orders flooded in, the admin dashboard became unusable, and customers complained about page timeouts. What was once a smooth-running site had become a nightmare.
This isn’t uncommon. WordPress powers nearly half the web because it’s so flexible and user-friendly. But that flexibility can become a problem when your site grows bigger and busier.
In this post, I’ll break down:
I’ve learned these lessons the hard way, so hopefully, my experience will save you some headaches!
Let’s start simple. WordPress uses two main database tables to store most of your content:
The wp_posts
table holds all your primary content:
Each entry has basic info like title, content, author, date, and status.
The wp_postmeta
table stores all the extra details about your posts. Think of it like a giant spreadsheet of additional information. For each piece of extra data you want to store, WordPress adds a new row in this spreadsheet:
Each row in this spreadsheet has just four columns:
This simple but powerful relationship - one post can have unlimited rows of extra data - is what makes WordPress so flexible.
This setup is a big reason for WordPress’s success:
When plugin developers want to add new features, they don’t need to mess with the database structure. They just add new rows of data to the postmeta table. This makes plugins much simpler to build and less likely to conflict with each other.
Need a job board? Property listings? Event calendar? The same posts/postmeta structure can handle it all. WordPress calls these “custom post types,” but they all use the same underlying system.
Unlike rigid systems where changing your data structure is a major project, with WordPress, you can add or remove custom fields whenever you want. No database restructuring required!
Take my client’s real estate website: we started with basic property listings but later decided to add mortgage calculators, virtual tour links, and neighborhood scores. Adding these new fields took minutes, not days.
So if this system is so great, where’s the catch? Performance.
As your site grows, the postmeta table typically grows much faster than the posts table. A site with 10,000 posts might have 200,000+ rows in the postmeta table!
Here’s why this becomes a problem:
Imagine trying to find all houses in a specific price range by searching through a giant spreadsheet with millions of rows, where each property detail lives in a separate row instead of having columns for price, bedrooms, etc. That’s essentially what WordPress has to do when searching through postmeta.
When WordPress needs to find posts with specific meta values (like products in a certain price range), it has to search through this massive table of extra data. As the table grows, these searches get slower and slower.
When WordPress’s default setup becomes too limiting, custom tables provide an alternative.
Instead of storing everything as generic posts with sticky notes, you create purpose-built tables specifically designed for your data.
Let’s use an online course website as an example:
With the default WordPress setup, you might have:
With custom tables, you’d create:
courses
table with columns for title, description, instructor, etc.student_enrollments
table linking students to coursescourse_progress
table tracking completionquiz_results
table for test scoresEach table is designed specifically for its purpose - just like using a spreadsheet instead of sticky notes.
The best way to understand the impact of WordPress’s database design is to look at WooCommerce, the most popular ecommerce plugin for WordPress.
From WooCommerce’s launch in 2011 until 2022, it relied entirely on WordPress’s standard database structure:
This approach initially made sense. It leveraged WordPress’s existing infrastructure and worked perfectly well for small to medium-sized stores.
As stores grew larger, serious performance issues began to appear:
Massive Database Bloat: A store with 10,000 orders might have 300,000+ rows in the postmeta table just for order data!
Slow Admin Screens: Store owners would wait 30+ seconds just to load their recent orders page.
Report Generation Timeouts: Generating sales reports would frequently time out, especially for date ranges longer than a month.
Checkout Slowdowns: During sales events, the database would become so overwhelmed that checkout processes would slow down or fail.
Expensive Queries: Simple operations like “find all orders from Customer X” required complex, resource-intensive database queries.
One store owner I worked with had just 15,000 orders but their wp_postmeta table had grown to 1.2 million rows, causing their entire site to become extremely slow and nearly unusable.
In August 2022, WooCommerce 6.9 introduced “High-Performance Order Storage” (HPOS) as an experimental feature. This was a complete redesign of how order data is stored, moving from the post/postmeta structure to dedicated custom tables:
wc_orders
- Main order information (ID, status, customer ID, totals)wc_order_addresses
- Billing and shipping detailswc_order_operational_data
- Processing metadatawc_order_items
- Products, shipping, fees in the orderwc_order_item_meta
- Additional item detailsThe performance improvements were dramatic:
The feature matured through several versions, becoming more stable in WooCommerce 7.1 (November 2022) and being widely recommended by WooCommerce 8.0 (August 2023).
This evolution represents a significant acknowledgment from the WooCommerce team that while WordPress’s flexible post/postmeta structure is great for content, it has serious limitations for transactional data like orders.
Importantly, WooCommerce hasn’t moved everything to custom tables - products still use the standard WordPress structure because they benefit from the content management features of posts. This hybrid approach demonstrates the nuanced decision-making required when scaling WordPress applications.
Custom tables can transform your site’s performance:
Instead of complex searches through the postmeta table, your queries become straightforward and efficient. One of my clients saw their product filtering speed improve by 400% after switching to custom tables.
Custom tables typically use much less space than the equivalent data in postmeta. This means faster backups and potentially lower hosting costs.
While the default WordPress structure tends to collapse under heavy traffic, custom tables maintain more consistent performance even with many simultaneous users.
Custom tables allow you to set rules about your data - like “this field can’t be empty” or “this must be a valid email address” - that aren’t possible with the generic postmeta setup.
Custom tables aren’t all sunshine and rainbows:
WordPress has dozens of helper functions for working with posts and meta. These won’t work with your custom tables, so you’ll need to write your own code to handle common tasks.
Many plugins expect to work with the standard WordPress setup. They might not be able to interact with your custom data unless you write additional code.
Creating and maintaining custom tables requires more technical knowledge and ongoing attention, especially during WordPress updates.
WordPress automatically caches post data, but you’ll need to implement your own caching for custom tables:
// Example of manual caching with custom tables
function get_my_course_data($id) {
// Try to get from cache first
$cached_data = wp_cache_get('course_' . $id, 'courses');
if ($cached_data) {
return $cached_data;
}
// Not in cache, fetch from database
global $wpdb;
$data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}courses WHERE id = {$id}");
// Save to cache for next time
wp_cache_set('course_' . $id, $data, 'courses');
return $data;
}
You don’t have to choose just one approach. In fact, the smartest strategy is often a mix:
This is exactly what WooCommerce does now - regular products use the standard WordPress structure, while orders use custom tables for better performance.
Here’s my practical advice after building dozens of WordPress sites:
Custom tables aren’t a magic bullet - they’re a targeted solution for specific scaling problems. Don’t add this complexity until you actually need it!
WordPress’s flexible post/postmeta structure is both its greatest strength and its biggest weakness when it comes to scaling. Understanding this fundamental trade-off helps you make smarter decisions about your website’s architecture.
If you’re experiencing performance issues with your WordPress site, consider whether custom tables might be the right solution for your specific data needs. Remember that you don’t have to choose just one approach - the most successful large WordPress sites often use a strategic combination of both methods.
If you found this article interesting, found errors, or just want to discuss about them, please get in touch.