Sample Header Ad - 728x90

MySQL 8 taking 1000x times longer to execute this query vs 5.7 when ORDER BY is used

0 votes
0 answers
925 views
This query comes from an ORM (Laravel Eloquent) on a WordPress database. I upgraded to MySQL 8 from 5.7 (AWS RDS), and the performance went from pretty speedy to basically broken. API requests that previously took ~50ms were now taking 30-60 *seconds* to execute. It was so bad I thought it was a DB connection issue at first. I used Laravel Telescope to diagnose and one particular type of query stood out immediately (see below). I then discovered that if I removed the order by, the issue went away completely! (Incidentally, if I removed the big subquery, the query was also fast). The query:
select
  *
from
  wp_posts
where
  post_type = 'post'
  and exists (
    select
      *
    from
      wp_term_taxonomy
      inner join wp_term_relationships on wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
    where
      wp_posts.ID = wp_term_relationships.object_id
      and taxonomy = 'tax_channel'
      and exists (
        select
          *
        from
          wp_terms
        where
          wp_term_taxonomy.term_id = wp_terms.term_id
          and slug in ('news')
      )
  )
  and (post_status = 'publish')

order by wp_posts.post_date desc

limit 2 offset 0
**explain results for 8.0: (takes ~30-90 seconds)** | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | |----|-------------|-----------------------|------------|--------|-----------------------------------|------------------|---------|---------------------------------------------|------|----------|---------------------------------------------------------------| | 1 | SIMPLE | wp_terms | | ref | PRIMARY,slug | slug | 766 | const | 2 | 100.00 | Using where; Using temporary; Using filesort; Start temporary | | 1 | SIMPLE | wp_term_taxonomy | | eq_ref | PRIMARY,term_id_taxonomy,taxonomy | term_id_taxonomy | 138 | wordpress.wp_terms.term_id,const | 1 | 100.00 | Using index | | 1 | SIMPLE | wp_term_relationships | | ref | PRIMARY,term_taxonomy_id | term_taxonomy_id | 8 | wordpress.wp_term_taxonomy.term_taxonomy_id | 238 | 100.00 | Using index | | 1 | SIMPLE | wp_posts | | eq_ref | PRIMARY,type_status_date | PRIMARY | 8 | wordpress.wp_term_relationships.object_id | 1 | 50.00 | Using where; End temporary | **Explain results for 5.7 (takes ~50ms)** | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | |----|--------------------|-----------------------|------------|--------|--------------------------|------------------|---------|--------------------------------------------------|--------|----------|-------------| | 1 | PRIMARY | wp_posts | | ref | type_status_date | type_status_date | 164 | const,const | 176172 | 100.00 | Using where | | 2 | DEPENDENT SUBQUERY | wp_term_relationships | | ref | PRIMARY,term_taxonomy_id | PRIMARY | 8 | wordpress.wp_posts.ID | 3 | 100.00 | Using index | | 2 | DEPENDENT SUBQUERY | wp_term_taxonomy | | eq_ref | PRIMARY,taxonomy | PRIMARY | 8 | wordpress.wp_term_relationships.term_taxonomy_id | 1 | 5.00 | Using where | | 3 | DEPENDENT SUBQUERY | wp_terms | | eq_ref | PRIMARY,slug | PRIMARY | 8 | wordpress.wp_term_taxonomy.term_id | 1 | 5.00 | Using where | **Explain results for 8.0 with order by removed (takes ~50ms)** | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | |----|-------------|-----------------------|------------|--------|-----------------------------------|------------------|---------|---------------------------------------------|------|----------|------------------------------| | 1 | SIMPLE | wp_terms | | ref | PRIMARY,slug | slug | 766 | const | 2 | 100.00 | Using where; Start temporary | | 1 | SIMPLE | wp_term_taxonomy | | eq_ref | PRIMARY,term_id_taxonomy,taxonomy | term_id_taxonomy | 138 | wordpress.wp_terms.term_id,const | 1 | 100.00 | Using index | | 1 | SIMPLE | wp_term_relationships | | ref | PRIMARY,term_taxonomy_id | term_taxonomy_id | 8 | wordpress.wp_term_taxonomy.term_taxonomy_id | 238 | 100.00 | Using index | | 1 | SIMPLE | wp_posts | | eq_ref | PRIMARY,type_status_date | PRIMARY | 8 | wordpress.wp_term_relationships.object_id | 1 | 50.00 | Using where; End temporary | I am looking for advice on how to bring MySQL 8 performance up to the level of 5.7 with this query. Or, a fix to the query that accomplishes the same thing. As I mentioned, this query is generated by an ORM (Eloquent) so I'm not sure how much control I have in changing the raw SQL. Or maybe there's something I'm missing in MySQL options table? Or, maybe I'm missing an index that could help besides the default WordPress keys?:
type_status_date	BTREE	FALSE	post_type,post_status,post_date,ID
post_parent_id	BTREE	FALSE	post_parent,ID
post_parent	BTREE	FALSE	post_parent
post_name	BTREE	FALSE	post_name(191)
post_author	BTREE	FALSE	post_author
PRIMARY	BTREE	TRUE	ID
**EDIT:** I've rewritten the above query as a series of JOINs instead of subqueries, but it is just as slow:
select * from wp_posts 
inner join wp_term_relationships on wp_posts.ID = wp_term_relationships.object_id
inner join wp_term_taxonomy on wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
inner join wp_terms on wp_term_taxonomy.term_id = wp_terms.term_id
where post_type = 'post'
and post_status = 'publish'
and slug in ('news')
order by post_date DESC
limit 2
**EDIT 2:** It looks like the cause of the slowness is that MySQL 8 is choosing the PRIMARY index instead of type_status_date. If I force it to use the latter, then the query is pretty speedy (~100ms). Still twice as high as 5.7 but at least it's in the ballpark. Because its an ORM writing this query, I dont think this solution is viable unfortunately. So adding /*+ index(wp_posts type_status_date) */ makes the query run in ~100ms instead of 30-90 seconds.
select /*+ index(wp_posts type_status_date) */
  *
from
  wp_posts

where
  post_type = 'post'
  and exists (
    select
      *
    from
      wp_term_taxonomy
      inner join wp_term_relationships on wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
    where
      wp_posts.ID = wp_term_relationships.object_id
      and taxonomy = 'tax_channel'
      and exists (
        select
          *
        from
          wp_terms
        where
          wp_term_taxonomy.term_id = wp_terms.term_id
          and slug in ('news')
      )
  )
  and (post_status = 'publish')
order by
  wp_posts.post_date desc
limit
  2 offset 0
Asked by Will (1 rep)
Mar 10, 2023, 06:33 AM
Last activity: Mar 10, 2023, 06:59 PM