MySQL。WordPress。在语句中使用时查询速度慢
我正试图找到一种更好的方法来编写以下由WordPress的MySQL。WordPress。在语句中使用时查询速度慢,mysql,performance,wordpress,Mysql,Performance,Wordpress,我正试图找到一种更好的方法来编写以下由WordPress的WP\u query类生成的查询。现在它非常慢 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
WP\u query
类生成的查询。现在它非常慢
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
我知道这看起来很混乱,但是当我没有WHERE子句(上面的mt5和mt6)中的2语句时,整个过程执行得非常快。问题是,我对SQL不太了解,无法找到另一种编写查询的方法来避免在
语句中使用。有什么想法吗
更新:
以下是此查询的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个以上的介于…和…
语句的组合都会对性能产生显著影响
例如,如果我删除最后2个和
子句,则查询执行时间约为0.04秒(与之相比为4.9秒);如果我删除带有in()
语句的2个和
子句,则查询执行时间约为0.04秒。这让我认为2查询解决方案可能是最好的,但我不知道如何通过WordPressWP_query
API实现,如果我实现了,我想知道这是否比只执行一个查询然后通过PHP过滤结果更快
我讨厌使用PHP进行过滤的想法,因为我在好几个地方读到过,过滤应该留给数据库,因为数据库擅长于此。顺便说一句,如果有什么不同的话,我正在我的localhost
WAMP服务器上的WordPress 3.3.1安装上运行这些查询,这台计算机具有强大的处理能力(Intel i7、12 GB RAM等)
更新3:
我想放弃并删除查询中的所有IN()
子句,并通过PHP对这些子句进行筛选,但这有一些严重的缺陷。除了效率低下和代码气味之外,它还不允许我正确控制分页。当数据库中的所有内容都被过滤后,我可以简单地使用LIMIT
子句来处理分页。当我使用PHP进行过滤时,我不知道对于任何给定的偏移量会返回多少结果。所以,所有的过滤都需要在数据库中进行,问题是如何进行。有人对我有任何其他建议吗?其他信息对任何人都有帮助吗
更新4:
在我寻找解决方案的过程中,我将其作为一个问题发布在WordPress core trac系统()中。那里的一位开发人员建议我尝试使用分类法而不是元数据来处理我在
元查询中使用的任何东西。我接受了这个建议,我看到了性能的提高,但不幸的是,这还远远不够。旧的查询需要4秒以上的时间运行,而使用分类法,它只需要1秒以上的时间。然而,我意识到我实际上需要4个类型子句(而不是原来的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),但是我对改进感到惊讶,这让我想知道是否有一个不同的解决方案可以进一步优化它。这是新的解释输出。它看起来几乎和我一样,但我真的不知道如何解释它
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+
| 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_值”字段是否有索引吗?这将大大提高速度
我不确定,但可能是CAST()函数导致了一些减速?它们是必要的吗?我现在偶然发现的“解决方案”非常难看,但不知为什么,它是有效的。添加直接连接
优化器提示将执行时间从18秒以上降低到大约0.0022秒。基于常识和这个问题(),这个解决方案似乎是个坏主意,但这是我尝试过的唯一有效的方法。所以,至少现在,我坚持这一点。如果有人对我为什么不应该这样做,或者我应该尝试什么有任何想法,我很乐意听到他们
如果有人好奇,我将其实现为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' );
为了完整起见,这里是使用直线连接时查询的解释
输出。我再一次感到困惑。旧的查询只使用了ref
和eq\u 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 |
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+
“meta_值”没有索引。我猜WordPress的开发人员并不认为这是必要的。CAST()函数对于大多数WHERE子句都是必需的,因为它们定义了十进制精度,而且更重要的是,当IN()为