opt*_*619 15 mysql indexing sql-order-by query-optimization
我正在使用Drupal 6与MySQL 5.0.95版本,并且陷入僵局,其中我的一个基于最近文章日期显示内容的查询速度变慢,并且由于使用频率导致网站性能完全丧失.有问题的查询如下:
SELECT n.nid,
n.title,
ma.field_article_date_format_value,
ma.field_article_summary_value
FROM node n
INNER JOIN content_type_article ma ON n.nid=ma.nid
INNER JOIN term_node tn ON n.nid=tn.nid
WHERE tn.tid= 153
AND n.status=1
ORDER BY ma.field_article_date_format_value DESC
LIMIT 0, 11;
Run Code Online (Sandbox Code Playgroud)
查询的EXPLAIN显示以下结果:
+----+-------------+-------+--------+--------------------------+---------+---------+----------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------+---------+---------+----------------------+-------+---------------------------------+
| 1 | SIMPLE | tn | ref | PRIMARY,nid | PRIMARY | 4 | const | 19006 | Using temporary; Using filesort |
| 1 | SIMPLE | ma | ref | nid,ix_article_date | nid | 4 | drupal_mm_stg.tn.nid | 1 | |
| 1 | SIMPLE | n | eq_ref | PRIMARY,node_status_type | PRIMARY | 4 | drupal_mm_stg.ma.nid | 1 | Using where |
+----+-------------+-------+--------+--------------------------+---------+---------+----------------------+-------+---------------------------------+
Run Code Online (Sandbox Code Playgroud)
该查询似乎相对简单和直接,并检索属于类别(术语)153且状态为1(已发布)的文章.但显然使用临时表和使用filesort意味着查询必然会从我学到的浏览中失败.
从ORDER BY子句中删除field_article_date_format_value可以解决Using temporary; 使用filesort减少了查询执行时间,但是是必需的,无法进行折衷,遗憾的是,同样适用于站点性能.
我的预感是,大多数问题来自term_node表,它将文章映射到类别,并且是一个多对多的关系表,这意味着如果文章X与5个类别C1相关联.C5它将在该表中有5个条目,这张桌子来自开箱即用的drupal.
处理繁重的数据库内容对我来说是新的,并且经历了一些类似的查询( 当按日期desc排序时,"使用临时"减慢查询速度, MySQL性能优化:按日期时间字段排序)我试图创建一个复合索引content_type_article其datetime字段在ORDER BY子句中使用,并且其中包含另一个键(nid)并尝试FORCE INDEX.
SELECT n.nid, n.title,
ma.field_article_date_format_value,
ma.field_article_summary_value
FROM node n
INNER JOIN content_type_article ma FORCE INDEX (ix_article_date) ON n.nid=ma.nid
INNER JOIN term_node tn ON n.nid=tn.nid
WHERE tn.tid= 153
AND n.status=1
ORDER BY ma.field_article_date_format_value DESC
LIMIT 0, 11;
Run Code Online (Sandbox Code Playgroud)
结果和以下EXPLAIN查询似乎没有多大帮助
+----+-------------+-------+--------+--------------------------+-----------------+---------+----------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------+-----------------+---------+----------------------+-------+---------------------------------+
| 1 | SIMPLE | tn | ref | PRIMARY,nid | PRIMARY | 4 | const | 18748 | Using temporary; Using filesort |
| 1 | SIMPLE | ma | ref | ix_article_date | ix_article_date | 4 | drupal_mm_stg.tn.nid | 1 | |
| 1 | SIMPLE | n | eq_ref | PRIMARY,node_status_type | PRIMARY | 4 | drupal_mm_stg.ma.nid | 1 | Using where |
+----+-------------+-------+--------+--------------------------+-----------------+---------+----------------------+-------+---------------------------------+
Run Code Online (Sandbox Code Playgroud)
字段n.nid,ca.nid,ma.field_article_date_format_value都已编入索引.使用ORDER BY子句查询带有限制0,11的DB大约需要7-10秒,但如果没有它,查询几乎不需要一秒钟.数据库引擎是MyISAM.任何有关这方面的帮助将不胜感激.
任何可以帮助我获得此查询的答案都像普通的那样(与没有按日期排序的查询速度相同)会很棒.我尝试用创建复合查询作为组合nid
并field_article_date_format_value
和使用查询没有帮助的原因.我愿意提供有关问题和任何新建议的其他信息.
小智 6
看一下你的查询和解释,似乎在where子句中使用n.status = 1会使搜索效率非常低,因为你需要返回连接定义的整个集合,然后应用status = 1.尝试从WHERE中立即过滤的term_node表开始连接,然后立即使连接添加状态条件.试一试,请告诉我它是怎么回事.
SELECT n.nid, n.title,
ma.field_article_date_format_value,
ma.field_article_summary_value
FROM term_node tn
INNER JOIN node n ON n.nid=tn.nid AND n.status=1
INNER JOIN content_type_article ma FORCE INDEX (ix_article_date) ON n.nid=ma.nid
WHERE tn.tid= 153
ORDER BY ma.field_article_date_format_value DESC
LIMIT 0, 11;
Run Code Online (Sandbox Code Playgroud)
MySQL 正在“优化”您的查询,以便它首先从 term_node 表中选择,即使您指定首先从节点中选择。由于不了解数据,我不确定哪种方法是最佳方法。term_node 表肯定是性能问题所在,因为从那里选择了大约 19,000 条记录。
没有 ORDER BY 的限制几乎总是更快,因为 MySQL 一旦找到指定的限制就会停止。使用 ORDER BY,它首先必须找到所有记录并对它们进行排序,然后获取指定的限制。
尝试的简单方法是将 WHERE 条件移至 JOIN 子句中,这是它应该在的位置。该过滤器特定于要连接的表。这将确保 MySQL 不会错误地优化它。
INNER JOIN term_node tn ON n.nid=tn.nid AND tn.tid=153
Run Code Online (Sandbox Code Playgroud)
更复杂的事情是在 term_node 表上执行 SELECT 并在其上进行 JOIN。这称为派生表,您将在 EXPLAIN 中看到它的定义。既然你说这是多对多,我添加了一个 DISTINCT 参数来减少要连接的记录数。
SELECT ...
FROM node n
INNER JOIN content_type_article ma FORCE INDEX (ix_article_date) ON n.nid=ma.nid
INNER JOIN (SELECT DISTINCT nid FROM term_node WHERE tid=153) tn ON n.nid=tn.nid
WHERE n.status=1
ORDER BY ma.field_article_date_format_value DESC
LIMIT 0,11
Run Code Online (Sandbox Code Playgroud)
MySQL 5.0 对派生表有一些限制,因此这可能不起作用。尽管有解决方法。
归档时间: |
|
查看次数: |
1050 次 |
最近记录: |