[wp-trac] [WordPress Trac] #50161: Slow query in WP_Posts_List_Table, no way to avoid with filters/hooks
WordPress Trac
noreply at wordpress.org
Thu Sep 4 11:53:34 UTC 2025
#50161: Slow query in WP_Posts_List_Table, no way to avoid with filters/hooks
--------------------------------+------------------------------------------
Reporter: LucasMS | Owner: SergeyBiryukov
Type: enhancement | Status: accepted
Priority: normal | Milestone: 6.9
Component: Posts, Post Types | Version: 5.4.1
Severity: normal | Resolution:
Keywords: has-patch 2nd- | Focuses: administration, performance
opinion |
--------------------------------+------------------------------------------
Comment (by jonsurrell):
I spent a lot of time to understand this and here are my findings:
- Unsurprisingly, the available indexes seem fine when there are not a lot
of rows. Queries on tables few rows are not particularly interesting
because they tend to be fast regardless.
- When the rows get into the hundreds of thousands, the query slows down
noticeably. [https://core.trac.wordpress.org/ticket/50161#comment:6
WordPress should remain performant well beyond this threshold.]
- I generated data and did a lot of testing. I found at least three
different compound keys that yield significant performance improvements in
the neighborhood of 3x faster.
Based on my findings I'm in favor of adding the new index as proposed in
[https://github.com/WordPress/wordpress-develop/pull/9312 PR 9312].
-----
[https://core.trac.wordpress.org/ticket/50161#comment:12 This is a
relevant point and one I was wondering about.] Is the new index more
broadly applicable, or can it be?
> Actually, the SQL inside of `wp_count_posts()` could probably be
rewritten to leverage this new index, as it's the same `post_type`,
`post_status`, `post_author` combo thanks to `readable` being used.
I tested this out on a smaller test site. I added three different indexes
(described below), monitored some queries with the Query Monitor plugin
and then explained them.
I observed that queries without `readable` continued to use an existing
index, but _do_ have the type_status_author index as a candidtate:
{{{
EXPLAIN SELECT post_status, COUNT( * ) AS num_posts FROM wp_posts WHERE
post_type = 'post' GROUP BY post_status;
+------+-------------+----------+------+-------------------------------------+------------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys
| key | key_len | ref | rows | Extra |
+------+-------------+----------+------+-------------------------------------+------------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | wp_posts | ref |
type_status_date,type_status_author | type_status_date | 82 | const |
217 | Using where; Using index |
+------+-------------+----------+------+-------------------------------------+------------------+---------+-------+------+--------------------------+
}}}
However, when I force the readable code path to be entered to adjust the
query, I observed the following:
{{{
EXPLAIN SELECT post_status, COUNT( * ) AS num_posts FROM wp_posts WHERE
post_type = 'post' AND (post_status != 'private' OR ( post_author = 1 AND
post_status = 'private' )) GROUP BY post_status;
+------+-------------+----------+-------+---------------------------------------------------------------------------------------+--------------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys
| key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------------------------------------------------------------------------------+--------------------+---------+------+------+--------------------------+
| 1 | SIMPLE | wp_posts | range |
type_status_date,post_author,type_status_author,author_type_status,type_author_status
| type_status_author | 172 | NULL | 218 | Using where; Using index |
+------+-------------+----------+-------+---------------------------------------------------------------------------------------+--------------------+---------+------+------+--------------------------+
}}}
The proposed `type_author_status` is used! The other indexes I tried were
also available in this case, but were not selected. This is more supports
my conclusion below that the proposed index is a good choice.
-----
I don't have a site available for benchmarking with sufficiently large
posts table, but I did some benchmarking locally that align with the
findings @josephscott
[https://core.trac.wordpress.org/ticket/50161#comment:7 shared above.]
I generated a posts table and inserted random data. Users were randomly
selected from 7 users, post types were random from the standard set of
post types, and so on for status, etc. The table contained 400,000 rows.
I tried a few different ways of benchmarking, primarily I used
[https://github.com/sharkdp/hyperfine hyperfine] to run the query via PHP
with or without different indexes.
I tried three different indexes:
- No new index (current WordPress Core).
- TSA as proposed by @josephscott `(post_type, post_status, post_author)`
- ATS `(post_author, post_type, post_status )`
- TAS `(post_type, post_author, post_status )`
In my test data, TSA, ATS, and TAS all seemed to yield a greater than 3x
improvement. ATS and TAS seemed to yield the largest speedup with a
negligible difference, while TSA had ''slightly'' smaller improvement.
Given that this was generated test data and not likely to follow real
world data patterns (for example, I doubt the distribution of post type,
author, and status is uniform) I'm confident enough that the performance
improvement is real and likely to mitigate the problem described in this
ticket for large sites. I'm not confident enough in the results to
recommend a specific index over another and would instead rely on the TSA
index that @josephscott selected based on working with real sites.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/50161#comment:31>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list