Custom post types are important, but they can kill your WordPress database performance. Also, there are three advanced optimizations that most developers miss.
The Hidden Performance Killer
WordPress stores custom- made post types in the same
wp_posts
table as regular posts and pages. With thousands of records, queries slow to a crawl because WordPress scans the entire table even when you only want one post type. Let’s fix that.
Optimization 1: Add a Composite Index on post_type + post_status
WordPress creates separate indexes on
post_type
and
post_status
, but most queries filter by BOTH. A composite index dramatically improves performance.
ALTER TABLE wp_posts
ADD INDEX idx_type_status (post_type, post_status);
Impact: Queries filtering by both fields run 3-5x faster. On a site with 50,000 posts, this reduced query time from 450ms to 90ms.
Pro tip: Add
post_date
as a third column if you frequently sort by date:
ADD INDEX idx_type_status_date (post_type, post_status, post_date);
Optimization 2: Cache Meta Query Results with Transients
Meta queries (filtering by custom fields) are notoriously slow because they join
wp_postmeta
to
wp_posts
. For queries that don’t change frequently, use transients.
function get_featured_products() {
$cache_key = ‘featured_products_v1’;
$products = get_transient($cache_key);
if (false === $products) {
$args = array(
‘post_type’ => ‘product’,
‘meta_query’ => array(
array(
‘key’ => ‘featured’,
‘value’ => ‘1’
)
)
);
$query = new WP_Query($args);
$products = $query->posts;
// Cache for 1 hour
set_transient($cache_key, $products, HOUR_IN_SECONDS);
}
return $products;
}
Critical: Invalidate the cache when posts are updated:
add_action(‘save_post_product’, function($post_id) {
delete_transient(‘featured_products_v1’);
});
Optimization 3: Use Direct SQL for Read-Heavy Operations
WP_Query is convenient but inefficient for simple counts or ID-only queries. Bypass it with direct SQL when you don’t need the full post object.
Slow way (WP_Query):
$query = new WP_Query(array(
‘post_type’ => ‘event’,
‘fields’ => ‘ids’,
‘posts_per_page’ => -1
));
$count = $query->found_posts; // Runs two queries
Fast way (direct SQL):
global $wpdb;
$count = $wpdb->get_var(“
SELECT COUNT(*)
FROM {$wpdb->posts}
WHERE post_type = ‘event’
AND post_status = ‘publish’
“); // Single optimized query
Impact: Direct SQL reduced query time from 180ms to 12ms for counting 15,000 custom posts.
Measuring Your Improvements
Use the Query Monitor plugin to track query performance before and after optimization. Look for:
- Total query execution time
- Number of queries per page load
- Slow queries (anything over 100ms)
Important Warning
Always back up your database before adding indexes—test on staging first. While indexes speed up reads, they slightly slow down writes (inserts/updates). For read-heavy sites, the tradeoff is worth it.
These three optimizations work together: composite indexes speed up queries, transients reduce query frequency, and direct SQL eliminates unnecessary overhead. Combined, they can reduce page load time by 60-80% on sites with large custom post type datasets.



