WordPress executes dozens of database queries per page load — fetching post data, option values, term counts, nav menus, and user metadata. The Object Cache is the in-memory key-value store that prevents identical queries from hitting the database twice within the same request. WordPress uses it internally for almost everything: get_post(), get_option(), get_terms(), and nav menu loading all populate and read the cache automatically. The default implementation stores data in a PHP array and lives only for the duration of the request. The real performance gain comes from a persistent backend — Redis or Memcached — which preserves cached data across requests and processes, turning a 200ms database query into a sub-millisecond memory read on every subsequent request. Using wp_cache_set(), wp_cache_get(), and proper cache groups in your own plugin code means your custom queries benefit from the same infrastructure that WordPress itself uses.
Problem: Your plugin runs an expensive custom $wpdb query on every page load — counting records, aggregating stats, or looking up configuration data — that returns the same result for minutes or hours at a time, causing unnecessary database load.
Solution: Wrap the expensive query with wp_cache_get() before executing it and wp_cache_set() after, using a descriptive cache key and an appropriate expiry. Invalidate the cache entry on save_post or whichever hook signals that the underlying data has changed.
<?php
/**
* Get the count of published posts in a custom post type, cached for 5 minutes.
*/
function get_published_resource_count() {
$cache_key = 'resource_count';
$cache_group = 'my_plugin';
$count = wp_cache_get( $cache_key, $cache_group );
if ( false === $count ) {
global $wpdb;
$count = (int) $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->posts}
WHERE post_type = 'resource' AND post_status = 'publish'"
);
// Cache for 5 minutes (300 seconds). 0 = cache indefinitely.
wp_cache_set( $cache_key, $count, $cache_group, 300 );
}
return $count;
}
// Invalidate when a 'resource' post is saved, published, or deleted
add_action( 'save_post_resource', 'invalidate_resource_count_cache' );
add_action( 'delete_post', 'invalidate_resource_count_cache' );
add_action( 'transition_post_status', function ( $new, $old ) {
if ( $new !== $old ) {
invalidate_resource_count_cache();
}
}, 10, 2 );
function invalidate_resource_count_cache() {
wp_cache_delete( 'resource_count', 'my_plugin' );
}
Cache groups make it easy to delete all entries for a plugin at once, and to avoid key collisions between plugins:
<?php
// Store per-user data under a namespaced key
$user_id = get_current_user_id();
$key = 'user_dashboard_data_' . $user_id;
$group = 'my_plugin_user';
$data = wp_cache_get( $key, $group );
if ( false === $data ) {
$data = expensive_user_query( $user_id );
wp_cache_set( $key, $data, $group, HOUR_IN_SECONDS );
}
// Check which backend is active
global $wp_object_cache;
$backend = get_class( $wp_object_cache );
// 'WP_Object_Cache' — default PHP array (per-request only)
// 'Redis_Object_Cache' etc. — persistent backend
NOTE: The default WordPress Object Cache is not persistent — it only survives for a single request. If you rely on wp_cache_set() to avoid repeat database hits across multiple requests, you must install a persistent cache drop-in (Redis Object Cache plugin, Memcached Object Cache). Without a persistent backend, wp_cache_get() always returns false on a fresh request and the underlying query runs every time. Verify the active backend with get_class( $wp_object_cache ) during development.