1

I'm using the WordPress theme Engine from Industrialthemes and see that the rendering of the front page uses a lot of queries that uses around 0.4 seconds to run in my MySQL database. Like this one:

SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1 =1
  AND (wp_term_relationships.term_taxonomy_id IN (1))
  AND wp_posts.post_type = 'post'
  AND (wp_posts.post_status = 'publish'
       OR wp_posts.post_status = 'closed')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC 
LIMIT 0,  5;

Is there any way this query can be improved? As far as I can see, the WordPress installation have the default indexes in place for all involved fields. My knowledge on tuning SQL Select statements is not good so I'm hoping for some experts to help me on this one. Thanks.

(From Comment)

CREATE TABLE wp_term_relationships (
    object_id bigint(20) unsigned NOT NULL DEFAULT '0', 
    term_taxonomy_id bigint(20) unsigned NOT NULL DEFAULT '0', 
    term_order int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (object_id,term_taxonomy_id), 
    KEY term_taxonomy_id (term_taxonomy_id), 
    KEY idx1 (object_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

(Later...)

CREATE TABLE wp_term_relationships (
    object_id bigint(20) unsigned NOT NULL DEFAULT '0', 
    term_taxonomy_id bigint(20) unsigned NOT NULL DEFAULT '0', 
    term_order int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (object_id,term_taxonomy_id), 
    KEY term_taxonomy_id (term_taxonomy_id), 
    KEY idx1 (term_taxonomy_id,object_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Rick James
  • 135,179
  • 13
  • 127
  • 222
Frank
  • 53
  • 4
  • No suggestions to improve this SELECT statement? Maybe it is impossible to make it better... – Frank Oct 20 '15 at 22:09

1 Answers1

1
wp_term_relationships needs INDEX(term_taxonomy_id, object_id) -- in this order
wp_posts might benefit from INDEX(post_type, ID, post_status, post_date) -- in this order

Both are "covering" indexes.
The former lets the JOIN work efficiently and gives the optimizer the option of starting with wp_term_relationships. And it should replace KEY term_taxonomy_id (term_taxonomy_id).
The latter should work well regardless of which table is picked first.

(More)

SELECT  SQL_NO_CACHE SQL_CALC_FOUND_ROWS p.ID
    FROM  wp_posts AS p
     WHERE p.post_type = 'post'
      AND  p.post_status IN ( 'publish', 'closed' )
      AND EXISTS ( SELECT 1 FROM wp_term_relationships AS tr
                     WHERE p.ID = tr.object_id
                       AND tr.term_taxonomy_id IN (1) )
    ORDER BY  p.post_date DESC
    LIMIT  0, 5;

With this formulation,

If the EXPLAIN starts with p:

p:  (post_date, post_type, post_status, ID)
p:  (post_type, post_status, ID, post_date)
tr:  (object_id, term_taxonomy_id)  -- which you have

If the EXPLAIN starts with tr:

p:  (ID)  -- which you probably have
tr:  (term_taxonomy_id, object_id)

The main problems:

  • The GROUP BY was adding effort. (I eliminated it by changing the JOIN to an EXISTS.)
  • IN ( 'publish', 'closed' ) -- inhibits effective use of index.
  • SQL_CALC_FOUND_ROWS -- means that it can't stop when it gets 5 rows.
  • IN (1) turn into = 1, which is fine; but IN (1,2) is messier.

Or, to be more blunt, WP has not yet been engineered to scale.

Please add the indexes and get the EXPLAIN SELECT.

From pastebin:

SELECT  SQL_NO_CACHE p.ID
    FROM  wp_posts AS p
    WHERE  p.post_type = 'post'
      AND  p.post_status = 'publish'
      AND  EXISTS 
    (
        SELECT  1
            FROM  wp_term_relationships AS tr
            WHERE  p.ID = tr.object_id
              AND  EXISTS 
            (
                SELECT  1
                    from  wp_term_taxonomy AS tt
                    WHERE  tr.term_taxonomy_id = tt.term_taxonomy_id
                      AND  tt.taxonomy = 'post_tag'
                      AND  tt.term_id IN (548, 669) ) 
    );

This is a different query. It needs this also:

tt:  INDEX(term_taxonomy_id, taxonomy,  -- in either order
           term_id)   -- last

And...

SELECT  SQL_NO_CACHE wp_posts.ID
    FROM  wp_posts
    INNER JOIN  wp_term_relationships tr 
           ON (wp_posts.ID = tr.object_id)
    INNER JOIN  wp_term_taxonomy tt
           ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
    WHERE  ( post_type = 'post'
              AND  post_status = 'publish'
              AND  tt.taxonomy = 'post_tag'
              AND  tt.term_id IN (548, 669)  
           )
    GROUP BY  wp_posts.ID;

needs

tt:  INDEX(taxonomy, term_id, term_taxonomy_id)  -- in this order

I would add both of those indexes to tt and see what happens to the EXPLAINs and to performance.

Rewrite query See if this gives you the 'right' answer:

SELECT  p.ID, p.post_name, p.post_title,
        p.post_type, p.post_status,
        tt.term_id as termid, tt.taxonomy
    FROM  wp_posts AS p
    INNER JOIN  wp_term_relationships tr  ON (p.ID = tr.object_id)
    INNER JOIN  wp_term_taxonomy tt  ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
    WHERE  p.post_type = 'post'
      AND  p.post_status = 'publish'
      AND  tt.taxonomy = 'post_tag'
      AND  tt.term_id IN (548, 669)
    ORDER BY  p.ID;

Notes:

  • GROUP BY removed
  • AND/OR probably not working as expected: a AND b OR c is equivalent to (a AND b) OR c, but I think you wanted a AND (b OR c)
  • Did you add the recommended indexes?
Rick James
  • 135,179
  • 13
  • 127
  • 222
  • index on wp_term_relationships breaks the site! – Micromega Feb 18 '19 at 12:46
  • @Bytemain - "Breaks the site"? The site is made of glass, and now there are shards littering the floor? Please elaborate. – Rick James Feb 18 '19 at 16:06
  • wp_term_relationships needs INDEX(term_taxonomy_id, object_id) doesn't work on my wordpress.index(object_id) works. – Micromega Feb 18 '19 at 16:35
  • @Bytemain - Please provide `SHOW CREATE TABLE wp_term_relationships`; maybe that will help me figure out the "doesn't work". – Rick James Feb 18 '19 at 16:52
  • wp_term_relationships | CREATE TABLE `wp_term_relationships` ( `object_id` bigint(20) unsigned NOT NULL DEFAULT '0', `term_taxonomy_id` bigint(20) unsigned NOT NULL DEFAULT '0', `term_order` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`object_id`,`term_taxonomy_id`), KEY `term_taxonomy_id` (`term_taxonomy_id`), KEY `idx1` (`object_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci – Micromega Feb 18 '19 at 17:09
  • Time to switch to InnoDB. – Rick James Feb 18 '19 at 17:21
  • I made a further stab. – Rick James Feb 18 '19 at 17:45
  • What? Yes or no? – Micromega Feb 18 '19 at 17:54
  • 1
    I added to my Answer. As for switching to InnoDB, it is probably safe to change on the fly, but it would be better to test separately. – Rick James Feb 18 '19 at 18:34
  • thx. i just tried it works (have backups). EDIT: I also add the index (object_id, term_taxonomy_id). it works but I have the index reverse: | wp_term_relationships | CREATE TABLE `wp_term_relationships` ( `object_id` bigint(20) unsigned NOT NULL DEFAULT '0', `term_taxonomy_id` bigint(20) unsigned NOT NULL DEFAULT '0', `term_order` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`object_id`,`term_taxonomy_id`), KEY `term_taxonomy_id` (`term_taxonomy_id`), KEY `idx1` (`term_taxonomy_id`,`object_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci | Is it bad? – Micromega Feb 18 '19 at 18:44
  • 1
    @Bytemain - I responded to your pastebin. – Rick James Mar 04 '19 at 01:01
  • Hi, thx, Yes, I have added the index. Basically the latter pastebin is the slow query. Here is an improved version, also with group by dropped and an alternate query (slower):https://pastebin.com/PzvqmNj3 – Micromega Mar 07 '19 at 15:28
  • Hi, I tested the recommended index on wp_term_taxonomy on my developer box and it seems to work, however when trying to implement it I had difficulties. Then I needed to drop the index fully but I think the query is now even faster! Here is the explain: https://pastebin.com/f0bj0NPR – Micromega Mar 07 '19 at 16:10
  • Hi, I have also mariadb on my production server and only mysql on my developer box. – Micromega Mar 07 '19 at 16:17
  • Hi, create index idx1 on wp_term_taxonomy(term_id,term_taxonomy_id); seems to work best. Can create index idx1 on wp_term_taxonomy(taxonomy, term_id, term_taxonomy_id) create problem? I tried it again but it seems it doesn't work. – Micromega Mar 07 '19 at 16:34
  • Hi, I have tried it with left join and it's a bit faster:https://pastebin.com/xQt33keX – Micromega Mar 07 '19 at 18:32
  • 1
    @Bytemain - The Optimizers in MySQL and MariaDB have diverged -- either might run a specific query faster than the other. – Rick James Mar 07 '19 at 20:13
  • I think I needed force index: https://stackoverflow.com/questions/14106770/how-can-i-avoid-a-full-table-scan-on-this-mysql-query. – Micromega Sep 07 '19 at 23:25