MySQL。 WordPress的。使用IN语句时缓慢的查询
我想弄清楚一个更好的方法来编写由WordPress的WP_Query
类生成的以下查询。现在是非常慢。MySQL。 WordPress的。使用IN语句时缓慢的查询
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
语句的另一种编写查询的方式。有任何想法吗?
UPDATE: 这里是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.9s他们),或它0.04秒,如果我把2个AND
条款与IN()
语句执行。这使我认为2查询解决方案可能是最好的,但我不知道如何通过WordPress WP_Query
API实现,如果我这样做,我不知道这是否会比只做一个查询,然后过滤结果通过PHP。
我讨厌使用PHP进行过滤的想法,因为我已经在几个地方阅读过滤应留给数据库,因为这是数据库擅长的地方。顺便说一句,如果它有什么区别,我在我的localhost
WAMP服务器上安装了WordPress 3.3.1安装程序,这些查询在具有足够处理能力(Intel i7,12 GB RAM等)的计算机上运行。
更新3: 我在想刚刚放弃和删除所有从查询和过滤那些通过PHP的IN()
条款,但有一些严重的抽奖背上。除了效率低下和代码味道之外,它不会让我正确控制分页。当数据库中的所有内容都被过滤时,我可以简单地使用LIMIT
子句来处理分页。当我使用PHP过滤时,我不知道对于任何给定的偏移量将返回多少结果。所以,所有的过滤都需要在数据库中完成,问题在于如何。有没有人对我有任何额外的建议?任何其他信息对任何人都有帮助吗?
UPDATE 4: 在我寻找一个解决的办法,我张贴在WordPress的核心TRAC系统(http://core.trac.wordpress.org/ticket/20134)的问题。其中一位开发人员建议我尝试在我的元查询中使用分类法而不是元数据来处理任何我使用IN
的元素。我接受了这个建议,我看到了性能的提高,但不幸的是,这还远远不够。旧的查询需要4秒以上的时间才能运行,并且使用分类法可以缩短到1秒以上。但是,我意识到我实际上需要4 IN
类型的子句(不是原来的2)。使用2个额外的分类法子句,查询需要18+秒钟才能执行。所以,我回到了原点。我认为有一种想法(这可能是妄想),是因为我的帖子太少,达不到标准。出于测试目的,我只在数据库中拥有'gemstone'
帖子类型的3个帖子。这有什么关系吗?
如果有人有兴趣,我的新的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:因为评论的 ,我最近在optimizing this query处再次刺伤,但我得出的结论是,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 |
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+
您可以检查“meta_value”字段是否有索引吗?这可能会大大提高速度。
我不太确定,但也许CAST()函数会导致一些放缓?他们有必要吗?
“meta_value”没有索引。我猜WordPress开发者并不认为这是必要的。 CAST()函数对于大多数WHERE子句是必需的,因为它们定义了十进制精度,而且更重要的是,当IN()语句不存在时,它们似乎不会放慢任何操作。我会做一些测试,但要确认。 – 2012-01-17 10:55:02
“meta_value”的索引本身并不是一个解决方案,因为它的结果不会直接用于比较,它总是传递给函数。 – newtover 2012-01-17 10:56:00
我刚刚在'meta_value'上添加索引后重新测试了查询,我可以确认索引没有效果。另外,为了彻底,我没有使用'CAST()'函数来尝试它,而且不幸的是它也没有效果。 – 2012-01-17 11:05:50
我现在偶然发现的“解决方案”非常难看,但出于某种莫名其妙的原因,它很有用。添加STRAIGHT_JOIN
优化程序提示将执行时间从18+秒减少到大约0.0022秒。基于常识和这个问题(When to use STRAIGHT_JOIN with 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');
以及物品是否完整,使用STRAIGHT_JOIN
的时候,这里是EXPLAIN
输出查询。再次,我很困惑。旧查询仅使用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 |
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+
作为更新。当我打开phpMyAdmin中的分析并运行此查询时,“统计数据”占用了99%的时间。有没有人知道“统计”在这方面的含义? – 2012-01-17 10:57:10
尝试删除额外的INNER JOIN,如我在这里解释的:http://*.com/a/15398104/212076 – KalenGi 2013-03-13 23:01:22