Post meta queries bring down databases, cripple traffic scaling, and dramatically inflate page load times. Thankfully there’s an almost trivial solution.

Searching for Meta

The problem comes when you need to query for post meta. This is a data design failure. Examples include:

A meta key for posts that should appear on the homepage

Marking a post as belonging to another post to be shown in a list

Bundling posts together into threads

Setting a type, such as the type of fabric of a post of type t-shirt

Setting an items colour

These are all examples of post meta that we want to search for. We might want to show only the red items, or the t-shirts made of cotton. These sound reasonable, but the query to find those posts with that meta can have monstrous performance costs.

If post meta was built to be searched, we’d be using it to store categories and tags.

Isn’t this just good/bad practice pedantry?

No, sites have been brought down by this, and it’s the reason a number of popular plugins don’t scale to high traffic. Install Query Monitor on a slow site, and you’ll see these queries. Page builders and meta box frameworks are particularly prone to enabling these kinds of queries.

Working at WordPress.com VIP, we see sites with meta queries handle poorly, and aren’t stable with medium/high traffic. Even in local environments, refactoring meta queries dramatically improves page load times. I’ve seen busy sites fall over after introducing simple meta queries.

This isn’t best practice or poor indentation. Meta queries cost real time and money. Adding more servers and faster CPUs won’t fix the problem because it’s a fundamentally expensive thing to do.

Can We Cache this?

Yes and no…

Caching will improve the best possible outcome, but you still have to run the query at least once. Coupled with timing issues your problem won’t be as big but you’ll still have a problem.

All of this is irrelevant though if your queries are too slow to finish, it’ll never complete in time to be cached

Can I Adjust The Database?

Yes and no..

You can add an Index to the post meta table, but this isn’t a true fix because:

the index will only store the beginning of the meta value, this works well for simple meta values such as a number, but for anything else it will be just as slow

the more you store the less efficient the index, storing the entire meta value negates the point of the index

It’s still slow and gives a less than stellar speed bump

You have to create the index yourself, requiring technical skill many don’t have ( some SQL foo )

A meta index may help if your meta value is a short numeric ID, but the gains are minimal

The Easy Fix

The problem is that these meta values should be terms in a custom taxonomy. Taxonomies are built to organise things and provide a way of filtering down to a specific set of posts. Post meta should only be used for information that isn’t going to be searched of filtered for.

Lets take our previous examples and look at how a custom taxonomy would be used:

Indicating that a post should appear on the homepage

The best way to do this is to have a custom taxonomy that controls where the post is shown, that has the homepage checked by default. A lot of people use post meta, and then go one step further by using post meta to say what they don’t want on the homepage. Those queries are orders of magnitude worse than a simple post meta query, and hideously expensive.

The best way to do this is to have a custom taxonomy that controls where the post is shown, that has the homepage checked by default. A lot of people use post meta, and then go one step further by using post meta to say what they don’t want on the homepage. Those queries are orders of magnitude worse than a simple post meta query, and hideously expensive. Marking a post as belonging to another post to be shown in a list, or bundling posts together into threads

A thread taxonomy where the name of each term is the Post ID of the original post, or a named thread. This taxonomy could be a hidden taxonomy with no UI that works behind the scenes

A thread taxonomy where the name of each term is the Post ID of the original post, or a named thread. This taxonomy could be a hidden taxonomy with no UI that works behind the scenes Setting a type, such as the type of fabric of a post of type t-shirt

A Fabric material taxonomy

A Fabric material taxonomy Setting an items colour

A colour taxonomy with terms for each colour

But What’s The Catch?

The upfront cost of converting your data from post meta to terms is the main downside. To do this I would use a WP CLI command to convert the data. Keep the post meta data around so that you can compare the two queries.

A taxonomy query is both simpler to write, significantly faster to run, and comes with free benefits, such as:

A database layout that’s optimised for these kinds of queries

The queries are much easier to type

A free UI in the admin interface with menu options

Term meta

Easy REST API support

A free set of archives, I have a talk taxonomy and you can see all my WordPress talks here, or all my WP CLI talks

A set of theme templates, taxonomy.php , taxonomy-{taxname}.php , taxonomy-{taxname}{termID}.php , etc

Taxonomies simplify things while providing plenty of benefits.

Further Reading