我有这个查询的问题:
SELECT a.*
FROM smartressort AS s
JOIN smartressort_to_ressort AS str
ON s.id = str.smartressort_id
JOIN article_to_ressort AS atr
ON str.ressort_id = atr.ressort_id
JOIN article AS a FORCE INDEX (source_created)
ON atr.article_id = a.id
WHERE
s.id = 1
ORDER BY
a.created_at DESC
LIMIT 25;
这个很慢,有时需要14秒。
EXPLAIN显示:
1 SIMPLE s const PRIMARY PRIMARY 4 const 1 Using index; Using temporary; Using filesort
1 SIMPLE str ref PRIMARY,ressort_id PRIMARY 4 const 1 Using index
1 SIMPLE atr ref PRIMARY,article_id PRIMARY 4 com.nps.lvz-prod.str.ressort_id 1262 Using index
1 SIMPLE a ALL NULL NULL NULL NULL 146677 Using where; Using join buffer (flat, BNL join)
所以最后一个“全部”类型真的很糟糕。但我已经试图强制使用索引,没有运气。
文章表如下所示:
CREATE TABLE `article` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`node_id` varchar(255) NOT NULL DEFAULT '',
`object_id` varchar(255) DEFAULT NULL,
`headline_1` varchar(255) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL,
`updated_at` datetime(3) NOT NULL,
`teaser_text` longtext NOT NULL,
`content_text` longtext NOT NULL,
PRIMARY KEY (`id`),
KEY `article_nodeid` (`node_id`),
KEY `article_objectid` (`object_id`),
KEY `source_created` (`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=161116 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
当我删除FORCE INDEX时,Explain变得更好,但查询仍然很慢。
解释没有力量指数:
1 SIMPLE s const PRIMARY PRIMARY 4 const 1 Using index; Using temporary; Using filesort
1 SIMPLE str ref PRIMARY,ressort_id PRIMARY 4 const 1 Using index
1 SIMPLE atr ref PRIMARY,article_id PRIMARY 4 com.nps.lvz-prod.str.ressort_id 1262 Using index
1 SIMPLE a eq_ref PRIMARY PRIMARY 4 com.nps.lvz-prod.atr.article_id 1
而对于另一个smartressort id(3),它看起来像这样:
1 SIMPLE s const PRIMARY PRIMARY 4 const 1 Using index; Using temporary; Using filesort
1 SIMPLE str ref PRIMARY,ressort_id PRIMARY 4 const 13 Using index
1 SIMPLE atr ref PRIMARY,article_id PRIMARY 4 com.nps.lvz-prod.str.ressort_id 1262 Using index
1 SIMPLE a eq_ref PRIMARY PRIMARY 4 com.nps.lvz-prod.atr.article_id 1
这里我们有一个Smartressort的13个Ressorts。行:1x1x13x1262x1 = 16.406
1)如何更快地提出此请求?
2)source_created
指数有什么问题?
您在查询中使用的SELECT *
很难看,这通常可以成为索引杀手。它可以排除索引的使用,因为您定义的大多数索引都不会覆盖SELECT *
所要求的每一列。这个答案的方法是索引查询中的所有其他表,这将激励MySQL只对article
表进行一次扫描。
CREATE INDEX idx1 ON article_to_ressort (article_id, ressort_id);
CREATE INDEX idx2 ON smartressort_to_ressort (ressort_id, smartressort_id);
这两个指数应加快加盟进程。请注意,我没有为smartressort
表定义索引,假设它的id
列已经是主键。我可能会从article
表开始编写您的查询,然后向外加入,但它应该不重要。
此外,强制索引主要是一个坏主意或不必要。优化器通常可以确定何时最好使用索引。
SELECT many columns FROM tables ORDER BY something LIMIT few
是一个臭名昭着的表演反模式;它必须检索并排序一堆乱七八糟的行和列,只是为了丢弃结果集中除了几行之外的所有行。
诀窍是找出结果集中需要的article.id
值,然后只检索这些值。它被称为延迟连接。
这应该得到你那套id
值。可能没有必要加入smartressort
表,因为smartressort_to_ressort
包含您需要的id
值。
SELECT a.id
FROM article a
JOIN article_to_ressort atr ON a.id = atr.article_id
JOIN smartressort_to_ressort str ON atr.ressort_id = str.ressort_id
WHERE str.smartressort_id = 1
ORDER BY a.created_at DESC
LIMIT 25
然后,您可以将其用作子查询来获取所需的行。
SELECT a.*
FROM article a
WHERE a.id IN (
SELECT a.id
FROM article a
JOIN article_to_ressort atr ON a.id = atr.article_id
JOIN smartressort_to_ressort str ON atr.ressort_id = str.ressort_id
WHERE str.smartressort_id = 1
ORDER BY a.created_at DESC
LIMIT 25
)
ORDER BY a.created_at DESC
第二个ORDER BY确保文章中的行具有可预测的顺序。那么,您的索引优化工作只需要应用于子查询。
除了@TimBiegelsen的好答案,我建议修改你的source_created
索引:
...
KEY `source_created` (`id`, `created_at`)
获得的是MySQL可以使用它进行排序,并且不需要获取所有16406行。它可能会或可能没有帮助,但值得尝试(可能使用明确的声明来使用它)
首先:您可以从查询中删除smartressort
表,因为它不会向其添加任何内容。
以下是您重写的查询。我们想要智能ressort#1的所有ressorts,然后是所有这些ressorts的文章。其中我们展示了最新的25个。
SELECT *
FROM article
WHERE id IN
(
SELECT article_id
FROM article_to_ressort
WHERE ressort_id IN
(
SELECT ressort_id
FROM smartressort_to_ressort
WHERE smartressort_id = 1
)
)
ORDER BY created_at DESC
LIMIT 25;
现在需要哪些索引来帮助DBMS?从内部表(smartressort_to_ressort
)开始。我们使用给定的smartressort_id
访问所有记录,我们想获得相关的ressort_id
。因此索引应按此顺序包含这两列。同样适用于article_to_ressort
及其ressort_id
和article_id
。最后,我们希望通过找到的文章ID和created_at
订购文章。
CREATE INDEX idx1 ON smartressort_to_ressort (smartressort_id, ressort_id);
CREATE INDEX idx2 ON article_to_ressort (ressort_id, article_id);
CREATE INDEX idx3 ON article (id, created_at);
无论如何,这些索引只是对DBMS的提议。它可能会决定反对他们。对于article
表上的索引尤其如此。 DBMS期望为一个smartressort_id
访问多少行,即IN
子句中可能有多少行?如果DBMS认为这可能大约是所有文章ID的10%,那么它可能已经决定顺序读取表,而不是混淆它通过索引这么多行。
所以对我来说解决方案是这样的:
SELECT a.*
FROM article as a USE INDEX (source_created)
where a.id in (
SELECT atr.article_id
from smartressort_to_ressort str
JOIN article_to_ressort atr ON atr.ressort_id = str.ressort_id
WHERE str.smartressort_id = 1
)
ORDER BY a.created_at DESC
LIMIT 25;
这只需要~35ms。说明看起来像这样:
1 PRIMARY a index NULL source_created 7 NULL 1
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
2 MATERIALIZED str ref PRIMARY,ressort_id,idx1 PRIMARY 4 const 1 Using index
2 MATERIALIZED atr ref PRIMARY,article_id,idx2 PRIMARY 4 com.nps.lvz-prod.str.ressort_id 1262 Using index
即便如此,这个查询Explain看起来对我来说更好,但我不知道为什么:
explain SELECT a.*, NOW()
FROM article as a USE INDEX (source_created)
where a.id in (SELECT atr.article_id
FROM smartressort AS s
JOIN smartressort_to_ressort AS str
ON s.id = str.smartressort_id
JOIN article_to_ressort AS atr
ON str.ressort_id = atr.ressort_id
WHERE s.id = 1
)
ORDER BY a.created_at DESC
LIMIT 25;
输出:
1 PRIMARY s const PRIMARY PRIMARY 4 const 1 Using index
1 PRIMARY a index NULL source_created 7 NULL 25
1 PRIMARY str ref PRIMARY,ressort_id,idx1 PRIMARY 4 const 1 Using index
1 PRIMARY atr eq_ref PRIMARY,article_id,idx2 PRIMARY 8 com.nps.lvz-prod.str.ressort_id,com.nps.lvz-prod.a.id 1 Using index; FirstMatch(a)