Matthew Boynes / @senyob / alley.co
https://mboynes.github.io/wc-portland-2018/
The most basic reason a query doesn’t perform well is because it’s working with too much data. Some queries just have to sift through a lot of data and can’t be helped. That’s unusual, though; most bad queries can be changed to access less data.
High Performance MySQL, 3rd Edition by Baron Schwartz; Peter Zaitsev; Vadim Tkachenko
What is the most basic way to speed up a database query?
Reduce the amount of data the database has to read.
(Specifically, B-tree indexes...)
High Performance MySQL, 3rd Edition by Baron Schwartz; Peter Zaitsev; Vadim Tkachenko
wp_posts
Keyname | Type | Field |
---|---|---|
PRIMARY | PRIMARY | ID |
post_name | INDEX | post_name |
type_status_date | INDEX | post_type post_status post_date ID |
post_parent | INDEX | post_parent |
post_author | INDEX | post_author |
wp_postmeta
Keyname | Type | Field |
---|---|---|
PRIMARY | PRIMARY | meta_id |
post_id | INDEX | post_id |
meta_key | INDEX | meta_key |
wp_term_relationships
Keyname | Type | Field |
---|---|---|
PRIMARY | PRIMARY | object_id term_taxonomy_id |
term_taxonomy_id | INDEX | term_taxonomy_id |
type_status_date
index?
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
WHERE 1=1
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
~73ms with 100k posts
Why is this so slow?
> EXPLAIN SELECT ... \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: wp_posts
type: range
possible_keys: type_status_date
key: type_status_date
key_len: 164
ref: NULL
rows: 49702
Extra: Using where; Using index; Using filesort
4ms with order/limit removed
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (2) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
~200ms with 100k posts in the term
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
LEFT JOIN wp_term_relationships AS tt1
ON (wp_posts.ID = tt1.object_id)
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (2)
AND tt1.term_taxonomy_id IN (9) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
~1ms with 100k posts in one term and 1 post in the other
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1
AND ( ( wp_postmeta.meta_key = 'city'
AND wp_postmeta.meta_value = 'portland' ) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
~410ms with 99,999 matching posts
~340ms with 1 matching post
A query is either fast or slow based on uniqueness of the meta key
When there's only 1 post with the 'city' key: 1ms
$query = new \WP_Query(
[
/* ... */
'date_query' => [
[
'after' => '-6 months',
],
],
]
);
meta_value
Transients API
$cache_key = 'popular_posts';
$post_ids = get_transient( $cache_key );
if ( false === $post_ids ) {
$post_ids = get_posts(
[
/* ... */
'fields' => 'ids',
]
);
set_transient( $cache_key, $post_ids, 10 * MINUTE_IN_SECONDS );
}
$posts = array_map( 'get_post', $post_ids );
Ideally, cache indefinitely and only clear when the data changes
function clear_popular_posts( $post_id, $post ) {
// Some logic determining if the cache should clear.
delete_transient( 'popular_posts' );
}
add_action( 'clean_post_cache', 'clear_popular_posts', 10, 2 );
add_action(
'save_post',
function() {
wp_schedule_single_event( time() + 5, 'warm_cache' );
}
);
add_action(
'warm_cache',
function() {
// Run laborious query and cache results.
}
);
Want to become a better developer?
We're hiring.
alley.co/careers