When working with WordPress sites that have a large number of users—especially in Multisite environments or setups that heavily rely on user meta—you may have noticed significant performance issues when querying users.
The primary reason for this is how WordPress loads user data:
• WP_User_Query retrieves user data from the database.
• Each user is instantiated as a WP_User object, which automatically loads all user meta and capability data into memory.
• If your site has extensive user meta, this leads to high memory usage and slow performance.
Thankfully, WordPress Core has introduced query caching in WP_User_Query, and there are proposals for lazy loading user meta and capability data. These improvements will make user queries significantly faster and more efficient.
The Problem: WP_User_Query Loads Too Much Data
When you use WP_User_Query, WordPress loads not just the basic user fields (like ID, user_email), but also:
• All user meta (even if you don’t need it)
• User capabilities (used for role-based permissions)
Example: Standard WP_User_Query Usage
$query = new WP_User_Query([
'role' => 'subscriber',
'number' => 50,
]);
$users = $query->get_results();
foreach ($users as $user) {
echo $user->user_email;
}
Problem: Even though we only need user_email, this query loads all user meta and capability data for every user, consuming unnecessary memory.
The Solution: Lazy Loading User Meta and Capabilities
WordPress Core is working on two major improvements to solve this:
Lazy Loading User Meta (Ticket #63021)
Currently, WP_User loads all user meta when instantiated. The proposed fix will defer this process, so metadata is only loaded when accessed.
This would reduce memory usage and improve query performance, especially for sites with extensive user meta.
Lazy Loading Capability Data (Ticket #58001)
Similar to user meta, capability data (stored in wp_usermeta) is often loaded even when it’s not needed. A proposed fix would make capabilities load only when requested, reducing unnecessary database queries.
WP_User_Query Query Caching (Introduced in WordPress 6.3)
Another major improvement is query caching in WP_User_Query, introduced in WordPress 6.3.
How Query Caching Works
• When a WP_User_Query is executed, the results are cached.
• Subsequent queries with the same parameters retrieve data from the cache instead of running a new database query.
• This significantly reduces database load and improves performance, especially for high-traffic sites.
Example: Default Query Caching
$args = [
'role' => 'subscriber',
'number' => 50,
];
$query = new WP_User_Query($args); // Query results are cached
Disabling Query Caching
If you need fresh data for a specific query, you can disable caching using cache_results => false:
$args = [
'number' => 50,
'cache_results' => false, // Force a fresh query
];
$query = new WP_User_Query($args);
Benefits of Query Caching
✅ Reduces Database Queries – Frequent user queries no longer hit the database.
✅ Improves Page Load Speed – Cached queries are retrieved instantly.
✅ Enhances WordPress Scalability – Essential for high-user and Multisite environments.
Should You Use Direct Queries Instead?
Instead of WP_User_Query, some developers use direct database queries for performance. Is this a good idea?
Recommended Approach: Use WP_User_Query with the fields Parameter
Rather than fetching full user objects, limit the fields retrieved:
$query = new WP_User_Query([
'role' => 'subscriber',
'number' => 50,
'fields' => ['ID', 'user_email'], // Only fetch needed fields
]);
$users = $query->get_results();
✅ Avoids loading unnecessary user meta and capabilities
✅ Still benefits from WordPress query caching
When to Use Direct Queries ($wpdb->get_results())
A direct query may be useful if:
• You need only a few fields and don’t want WP_User overhead.
• You need an aggregated result (e.g., counting users).
• You’re running a custom, high-performance query.
Example: Fetching user IDs and emails directly:
global $wpdb;
$users = $wpdb->get_results("SELECT ID, user_email FROM {$wpdb->users} WHERE user_status = 0");
foreach ($users as $user) {
echo $user->user_email;
}
❌ Bypasses WordPress caching and hooks
✅ Fastest method when WordPress overhead isn’t needed
Best Practice:
• Use WP_User_Query with fields when possible.
• Use $wpdb->get_results() only when you need full control over queries.
Summary: How to Optimise WP_User_Query Performance
✅ Use Query Caching (Enabled by Default in WP 6.3)
• WordPress now caches WP_User_Query results automatically.
• This reduces database load and speeds up user queries.
✅ Limit Fields in WP_User_Query
• Use ‘fields’ => [‘ID’, ‘user_email’] to avoid unnecessary data.
✅ Lazy Loading for User Meta and Capabilities (Coming Soon!)
• WordPress Core is working on making user meta and capabilities load only when accessed.
✅ Use Direct Queries Only When Necessary
• If you don’t need WordPress hooks/caching, $wpdb->get_results() is an option.
Final Thoughts
These improvements—query caching and lazy loading—will make WordPress more efficient, especially for large user-heavy sites and Multisite installations.
By applying these best practices, you can:
✅ Reduce memory usage
✅ Improve page speed
✅ Lower database queries
Want to test these improvements? Keep an eye on the WordPress Core development tickets for when lazy loading gets merged!
You must be logged in to post a comment.