MySQL.WordPress.Медленный запрос при использовании операторов IN - PullRequest
1 голос
/ 17 января 2012

Я пытаюсь найти лучший способ написать следующий запрос, сгенерированный классом WP_Query WordPress.Прямо сейчас это очень медленно.

SELECT SQL_CALC_FOUND_ROWS wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id)
INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id)
INNER JOIN wp_postmeta AS mt4 ON (wp_posts.ID = mt4.post_id)
INNER JOIN wp_postmeta AS mt5 ON (wp_posts.ID = mt5.post_id)
INNER JOIN wp_postmeta AS mt6 ON (wp_posts.ID = mt6.post_id)
INNER JOIN wp_postmeta AS mt7 ON (wp_posts.ID = mt7.post_id)
INNER JOIN wp_postmeta AS mt8 ON (wp_posts.ID = mt8.post_id)

WHERE 1=1 AND wp_posts.post_type = 'gemstone'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'gemstone_active_price'
    AND (mt1.meta_key = 'gemstone_status' AND CAST(mt1.meta_value AS CHAR) = 'Available')
    AND (mt2.meta_key = 'gemstone_length' AND CAST(mt2.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '9')
    AND (mt3.meta_key = 'gemstone_width' AND CAST(mt3.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '9')
    AND (mt4.meta_key = 'gemstone_depth' AND CAST(mt4.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '7')
    AND (mt5.meta_key = 'gemstone_color' AND CAST(mt5.meta_value AS CHAR) IN ('L','K','J','I','H','G','F','E','D'))
    AND (mt6.meta_key = 'gemstone_clarity' AND CAST(mt6.meta_value AS CHAR) IN ('I3','I2','I1','SI2','SI1','VS2','VVS2','VVS1','IF','FL'))
    AND (mt7.meta_key = 'gemstone_weight' AND CAST(mt7.meta_value AS DECIMAL(10,2)) BETWEEN '0.67' AND '1.85')
    AND (mt8.meta_key = 'gemstone_active_price' AND CAST(mt8.meta_value AS DECIMAL(10,2)) BETWEEN '960' AND '2300')
)

GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value+0 ASC
LIMIT 0, 20

Я знаю, это выглядит как большой беспорядок, но все это выполняется очень быстро, когда у меня нет операторов 2 INв предложении WHERE (MT5 и MT6 выше).Проблема в том, что я недостаточно хорошо знаю SQL, чтобы найти другой способ написания запроса, в котором не используются операторы IN.Есть идеи?

ОБНОВЛЕНИЕ: Вот вывод EXPLAIN для этого запроса на случай, если он кому-нибудь поможет.Если у кого-то есть другие мысли, я открыт для всего.Это поставило меня в тупик.

id  select_type     table           type    possible_keys               key         key_len     ref                         rows    Extra
1   SIMPLE          wp_postmeta     ref     post_id,meta_key            meta_key    768         const                       2       Using where; Using temporary; Using filesort
1   SIMPLE          mt1             ref     post_id,meta_key            post_id     8           db.wp_postmeta.post_id      2       Using where
1   SIMPLE          mt2             ref     post_id,meta_key            post_id     8           db.mt1.post_id              2       Using where
1   SIMPLE          mt3             ref     post_id,meta_key            post_id     8           db.wp_postmeta.post_id      2       Using where
1   SIMPLE          mt4             ref     post_id,meta_key            post_id     8           db.mt2.post_id              2       Using where
1   SIMPLE          mt5             ref     post_id,meta_key            post_id     8           db.wp_postmeta.post_id      2       Using where
1   SIMPLE          mt6             ref     post_id,meta_key            post_id     8           db.wp_postmeta.post_id      2       Using where
1   SIMPLE          mt7             ref     post_id,meta_key            post_id     8           db.mt3.post_id              2       Using where
1   SIMPLE          mt8             ref     post_id,meta_key            post_id     8           db.wp_postmeta.post_id      2       Using where
1   SIMPLE          wp_posts        eq_ref  PRIMARY,type_status_date    PRIMARY     8           db.wp_postmeta.post_id      1       Using where

ОБНОВЛЕНИЕ 2: После дополнительных экспериментов я понял, что не только операторы IN() замедляют этот запрос.Кажется, что любая комбинация из более чем одного IN() с более чем 3 BETWEEN...AND... операторов оказывает существенное влияние на производительность.

Например, запрос выполняется примерно за 0,04 с, если я отбрасываю последние 2 AND предложений (против 4,9 с ними), или он выполняется за 0,04 с, если я отбрасываю 2 AND предложений с операторами IN().Это заставляет меня думать, что решение с двумя запросами может быть лучшим, но я понятия не имею, как реализовать это через API WordPress WP_Query, и если бы я это сделал, я бы подумал, что это будет быстрее, чем просто сделать один запрос изатем фильтровать результаты с помощью PHP.

Я ненавижу идею фильтрации с помощью PHP, потому что я прочитал в нескольких местах, что фильтрация должна быть оставлена ​​на базу данных, потому что это то, что базы данных хороши.Кстати, если это имеет какое-то значение, я выполняю эти запросы на установке WordPress 3.3.1 на моем localhost WAMP-сервере на компьютере с большим количеством вычислительной мощности (Intel i7, 12 ГБ ОЗУ и т. Д.).

ОБНОВЛЕНИЕ 3: Я думал о том, чтобы просто отказаться от всех предложений IN() и удалить их из запроса и отфильтровать их с помощью PHP, но у этого есть некоторые серьезные недостатки.Помимо неэффективности и запаха кода, это не позволит мне правильно контролировать свою нумерацию страниц.Когда все фильтруется в базе данных, я могу просто использовать предложение LIMIT для разбивки на страницы.Когда я фильтрую с помощью PHP, я понятия не имею, сколько результатов будет возвращено для любого заданного смещения.Итак, вся фильтрация действительно должна выполняться в базе данных, вопрос в том, как это сделать.У кого-нибудь есть какие-нибудь дополнительные предложения для меня?Будет ли какая-либо дополнительная информация полезной для кого-либо?

ОБНОВЛЕНИЕ 4: В поисках решения этой проблемы я разместил ее как проблему в основной системе отслеживания WordPress (http://core.trac.wordpress.org/ticket/20134). Один из разработчиков предложил мне использовать таксономии вместо метаданных для всего, для чего я использую IN в своем мета-запросе. Я воспользовался этим советом и увидел улучшение производительности, но, к сожалению, это не такпочти достаточно. Для выполнения старого запроса потребовалось более 4 секунд, а при использовании таксономий он сократился до 1 секунды. Однако я понял, что мне действительно нужны предложения типа 4 IN (а не исходные 2). С 2 дополнительными таксономиямипункты, выполнение запроса занимает более 18 секунд. Итак, я вернулся к исходной точке. Одна мысль, которая у меня была (это, вероятно, бред), заключается в том, что это может быть настолько медленным, потому что у меня так мало сообщений, которые соответствуют критериям.В целях тестирования у меня есть только 3 сообщения в базе данных, которые имеют тип сообщения 'gemstone'. Может ли это иметь какое-либо отношение к этому?

Если кто-тоинтересно, мой новый SQL выглядит так:

SELECT SQL_CALC_FOUND_ROWS wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id)
INNER JOIN wp_term_relationships AS tt2 ON (wp_posts.ID = tt2.object_id)
INNER JOIN wp_term_relationships AS tt3 ON (wp_posts.ID = tt3.object_id)    
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id)
INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id)
INNER JOIN wp_postmeta AS mt4 ON (wp_posts.ID = mt4.post_id)
INNER JOIN wp_postmeta AS mt5 ON (wp_posts.ID = mt5.post_id)
INNER JOIN wp_postmeta AS mt6 ON (wp_posts.ID = mt6.post_id)

WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (71,72,73,74)
    AND tt1.term_taxonomy_id IN (89,90,91,92,93,95,96,97)
    AND tt2.term_taxonomy_id IN (56,50,104,53)
    AND tt3.term_taxonomy_id IN (59,60,62)
)
AND wp_posts.post_type = 'gemstone'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'gemstone_weight'
    AND (mt1.meta_key = 'gemstone_status' AND CAST(mt1.meta_value AS CHAR) = 'Available')
    AND (mt2.meta_key = 'gemstone_length' AND CAST(mt2.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '9')
    AND (mt3.meta_key = 'gemstone_width' AND CAST(mt3.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '9' )
    AND (mt4.meta_key = 'gemstone_depth' AND CAST(mt4.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '7')
    AND (mt5.meta_key = 'gemstone_weight' AND CAST(mt5.meta_value AS DECIMAL(8,2)) BETWEEN '0.81' AND '1.81')
    AND (mt6.meta_key = 'gemstone_active_price' AND CAST(mt6.meta_value AS DECIMAL(8,2)) BETWEEN '1083.9' AND '2078.26')
)

GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value+0 ASC
LIMIT 0, 20

, а новый вывод EXPLAIN выглядит следующим образом:

id  select_type     table                   type    possible_keys               key         key_len     ref                     rows    Extra
1   SIMPLE          wp_postmeta             ref     post_id,meta_key            meta_key    768         const                   3       Using where; Using temporary; Using filesort
1   SIMPLE          tt3                     ref     PRIMARY,term_taxonomy_id    PRIMARY     8           db.wp_postmeta.post_id  1       Using where; Using index
1   SIMPLE          tt2                     ref     PRIMARY,term_taxonomy_id    PRIMARY     8           db.wp_postmeta.post_id  1       Using where; Using index
1   SIMPLE          wp_term_relationships   ref     PRIMARY,term_taxonomy_id    PRIMARY     8           db.tt2.object_id        1       Using where; Using index
1   SIMPLE          wp_posts                eq_ref  PRIMARY,type_status_date    PRIMARY     8           db.wp_postmeta.post_id  1       Using where
1   SIMPLE          tt1                     ref     PRIMARY,term_taxonomy_id    PRIMARY     8           db.wp_posts.ID          1       Using where; Using index
1   SIMPLE          mt5                     ref     post_id,meta_key            post_id     8           db.wp_posts.ID          2       Using where
1   SIMPLE          mt6                     ref     post_id,meta_key            post_id     8           db.wp_posts.ID          2       Using where
1   SIMPLE          mt1                     ref     post_id,meta_key            post_id     8           db.mt5.post_id          2       Using where
1   SIMPLE          mt2                     ref     post_id,meta_key            post_id     8           db.mt1.post_id          2       Using where
1   SIMPLE          mt3                     ref     post_id,meta_key            post_id     8           db.tt2.object_id        2       Using where
1   SIMPLE          mt4                     ref     post_id,meta_key            post_id     8           db.tt3.object_id        2       Using where

ОБНОВЛЕНИЕ 5: Из-за комментария я недавно сделал еще один шаг в , оптимизируя этот запрос , но я остался с выводом, что SQL в значительной степени должен быть настроен таким, какой он есть.Однако, тестируя некоторые альтернативы, я обнаружил, что теперь, как ни странно, запрос выполняется намного быстрееЯ не обновил свой сервер MySQL, поэтому единственная причина, по которой я могу это понять, заключается в том, что WordPress каким-то образом обновил структуру своей базы данных, чтобы повысить производительность.Точно такой же запрос, показанный в обновлении 4, теперь занимает примерно 2,4 секунды.На мой взгляд, все еще слишком долго (поэтому я все еще использую STRAIGHT_JOIN, как показано в моем ответе ниже), но я был удивлен улучшением, и это заставляет меня задуматься, есть ли другое решение, способное оптимизировать это еще больше.Вот новый выход EXPLAIN.Это выглядит почти идентично мне, но я не знаю, как это интерпретировать.

+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+
| id  | select_type  |         table          |  type   |      possible_keys        |   key     | key_len  |                ref                  | rows  |                    Extra                     |
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+
|  1  | SIMPLE       | wp_postmeta            | ref     | post_id,meta_key          | meta_key  |     768  | const                               |    5  | Using where; Using temporary; Using filesort |
|  1  | SIMPLE       | wp_term_relationships  | ref     | PRIMARY,term_taxonomy_id  | PRIMARY   |       8  | db.wp_postmeta.post_id              |    1  | Using where; Using index                     |
|  1  | SIMPLE       | tt2                    | ref     | PRIMARY,term_taxonomy_id  | PRIMARY   |       8  | db.wp_term_relationships.object_id  |    1  | Using where; Using index                     |
|  1  | SIMPLE       | tt3                    | ref     | PRIMARY,term_taxonomy_id  | PRIMARY   |       8  | db.wp_term_relationships.object_id  |    1  | Using where; Using index                     |
|  1  | SIMPLE       | wp_posts               | eq_ref  | PRIMARY,type_status_date  | PRIMARY   |       8  | db.wp_postmeta.post_id              |    1  | Using where                                  |
|  1  | SIMPLE       | tt1                    | ref     | PRIMARY,term_taxonomy_id  | PRIMARY   |       8  | db.wp_posts.ID                      |    1  | Using where; Using index                     |
|  1  | SIMPLE       | mt3                    | ref     | post_id,meta_key          | post_id   |       8  | db.tt2.object_id                    |    3  | Using where                                  |
|  1  | SIMPLE       | mt4                    | ref     | post_id,meta_key          | post_id   |       8  | db.tt3.object_id                    |    3  | Using where                                  |
|  1  | SIMPLE       | mt5                    | ref     | post_id,meta_key          | post_id   |       8  | db.wp_posts.ID                      |    3  | Using where                                  |
|  1  | SIMPLE       | mt6                    | ref     | post_id,meta_key          | post_id   |       8  | db.wp_posts.ID                      |    3  | Using where                                  |
|  1  | SIMPLE       | mt1                    | ref     | post_id,meta_key          | post_id   |       8  | db.mt5.post_id                      |    3  | Using where                                  |
|  1  | SIMPLE       | mt2                    | ref     | post_id,meta_key          | post_id   |       8  | db.mt3.post_id                      |    3  | Using where                                  |
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+

Ответы [ 2 ]

3 голосов
/ 06 марта 2012

«Решение», на которое я наткнулся на данный момент, довольно уродливо, но по какой-то необъяснимой причине оно работает.Добавление подсказки оптимизатора STRAIGHT_JOIN сократило время выполнения с 18+ секунд до примерно 0,0022 секунд.Исходя из здравого смысла и этого вопроса ( Когда использовать STRAIGHT_JOIN с MySQL ), это решение кажется плохой идеей, но это единственное, что я пробовал, сработало.Так что, по крайней мере, сейчас я придерживаюсь этого.Если у кого-то есть мысли о том, почему я не должен этого делать, или о том, что я должен попробовать вместо этого, я хотел бы услышать их.

Если кому-то интересно, я реализовал это как фильтр WordPress следующим образом:

function use_straight_join( $distinct_clause ) {

    $distinct_clause = ( $use_straight_join ) ? 'STRAIGHT_JOIN' . $distinct_clause : $distinct_clause;

    return $distinct_clause;
}
add_filter( 'posts_distinct', 'use_straight_join' );

А для полноты вот вывод EXPLAIN для запроса при использовании STRAIGHT_JOIN.Я опять сбит с толку.Старый запрос использовал только ref и eq_ref, которые, как я понимаю, быстрее, чем range, но по некоторым причинам это на несколько порядков быстрее.

+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+
| id  | select_type  |         table          | type   |      possible_keys        |       key         | key_len  |      ref        | rows  |                    Extra                     |
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+
|  1  | SIMPLE       | wp_posts               | range  | PRIMARY,type_status_date  | type_status_date  |     124  | NULL            |    6  | Using where; Using temporary; Using filesort |
|  1  | SIMPLE       | wp_postmeta            | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | mt1                    | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | mt2                    | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | mt3                    | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | mt4                    | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | mt5                    | ref    | post_id,meta_key          | post_id           |       8  | db.mt3.post_id  |    2  | Using where                                  |
|  1  | SIMPLE       | mt6                    | ref    | post_id,meta_key          | post_id           |       8  | db.wp_posts.ID  |    2  | Using where                                  |
|  1  | SIMPLE       | wp_term_relationships  | ref    | PRIMARY,term_taxonomy_id  | PRIMARY           |       8  | db.wp_posts.ID  |    1  | Using where; Using index                     |
|  1  | SIMPLE       | tt1                    | ref    | PRIMARY,term_taxonomy_id  | PRIMARY           |       8  | db.wp_posts.ID  |    1  | Using where; Using index                     |
|  1  | SIMPLE       | tt2                    | ref    | PRIMARY,term_taxonomy_id  | PRIMARY           |       8  | db.mt1.post_id  |    1  | Using where; Using index                     |
|  1  | SIMPLE       | tt3                    | ref    | PRIMARY,term_taxonomy_id  | PRIMARY           |       8  | db.wp_posts.ID  |    1  | Using where; Using index                     |
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+
0 голосов
/ 17 января 2012

Можете ли вы проверить, имеет ли поле "meta_value" индекс? Это может значительно улучшить скорость.

Я не уверен в этом, но, возможно, функции CAST () вызывают некоторое замедление? Они необходимы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...