我有以下查询:
SELECT
m.id_ AS match_id,
mv.id_ AS match_version_id,
t.id_ AS tournament_id
FROM
test.match_version AS mv
JOIN
test.match_ AS m ON m.id_ = mv.match_id
JOIN
test.tournament AS t ON t.orig_id = m.tournament_orig_id
AND t.tour_id = m.tour_id
ORDER BY mv.id_ DESC
LIMIT 100;
字符串
在比赛表具有大约1 m行并且锦标赛表具有大约30 k行的情况下,运行大约需要4秒。如果我删除ORDER_BY
,那么它将在0.002秒内运行。
下面是每个表的CREATE TABLE
语句:
CREATE TABLE `match_` (
`id_` int NOT NULL AUTO_INCREMENT,
`tour_id` tinyint NOT NULL,
`tournament_orig_id` int NOT NULL,
PRIMARY KEY (`id_`),
KEY `ix__match__tour_id` (`tour_id`),
KEY `ix__match__tour_id__tournament_orig_id` (`tour_id`,`tournament_orig_id`),
KEY `ix__match__tournament_orig_id` (`tournament_orig_id`),
CONSTRAINT `fk__match__tournament` FOREIGN KEY (`tour_id`, `tournament_orig_id`) REFERENCES `tournament` (`tour_id`, `orig_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1666470 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `match_version` (
`id_` int NOT NULL AUTO_INCREMENT,
`match_id` int NOT NULL,
`generation_start` int NOT NULL DEFAULT '0',
`generation_end` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id_`),
KEY `ix__match_version__match_id` (`match_id`),
KEY `ix__match_version__match_id__generation_end` (`match_id`,`generation_end`),
KEY `ix__match_version__generation_end` (`generation_end`),
CONSTRAINT `fk__match_version__match_id` FOREIGN KEY (`match_id`) REFERENCES `match_` (`id_`)
) ENGINE=InnoDB AUTO_INCREMENT=1669206 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `tournament` (
`id_` int NOT NULL AUTO_INCREMENT,
`tour_id` tinyint NOT NULL,
`orig_id` int NOT NULL,
PRIMARY KEY (`id_`),
UNIQUE KEY `uq__tournament_oc__tour_id__orig_id` (`tour_id`,`orig_id`),
KEY `ix__tournament_oc__tour_id` (`tour_id`),
KEY `ix__tournament_oc__orig_id` (`orig_id`)
) ENGINE=InnoDB AUTO_INCREMENT=30962 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
型
下面是EXPLAIN
的结果:
+----+-------------+-------+------------+-------+-------------------------------------------------------------------------------------------------+----------------------------------------+---------+-------------------------------+-------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+-------------------------------------------------------------------------------------------------+----------------------------------------+---------+-------------------------------+-------+----------+----------------------------------------------+
| 1 | SIMPLE | t | NULL | index | uq__tournament_oc__tour_id__orig_id,ix__tournament_oc__tour_id,ix__tournament_oc__orig_id | uq__tournament_oc__tour_id__orig_id | 5 | NULL | 30900 | 100 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | m | NULL | ref | PRIMARY,ix__match__tour_id,ix__match__tour_id__tournament_orig_id,ix__match__tournament_orig_id | ix__match__tour_id__tournament_orig_id | 5 | test.t.tour_id,test.t.orig_id | 48 | 100 | Using index |
| 1 | SIMPLE | mv | NULL | ref | ix__match_version__match_id,ix__match_version__match_id__generation_end | ix__match_version__match_id | 4 | test.m.id_ | 1 | 100 | Using index |
+----+-------------+-------+------------+-------+-------------------------------------------------------------------------------------------------+----------------------------------------+---------+-------------------------------+-------+----------+----------------------------------------------+
型
为什么当ORDER_BY
存在时,我的性能会变得相当差?我该怎么解决这个问题?
注意:值得一提的是,我知道没有tournament_version.tournament_id
外键链接到tournament.id_
是不理想的,但可悲的是,我正在使用atm!
3条答案
按热度按时间13z8s7eq1#
您的查询似乎遇到了mysql order by limit slow rate look up的问题。不幸的是,这是mysql中缺少的优化。
请参阅此处的其他详细信息以了解此查询为何缓慢。在页面的底部,它显示了一些如何加速查询的策略,如下所示:https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
如果这不起作用,您可以尝试重新构造查询,使其不同时包含ORDER BY和LIMIT。正如你可能注意到从玩弄你的查询,如果你可以删除其中任何一个关键字,它会很快了。
z9smfwbn2#
感谢@tay_inc提供的link。以下代码将在0.04秒内执行:
字符串
我能看到的唯一问题是,这会获取
mv
中的最后100条记录,而不管它们是否可以连接到m
和t
。在这种情况下对我来说不是问题,但值得记住...fjnneemd3#
我认为你的索引是好的,但有一些额外的你应该摆脱。
因为
id_
是PRIAMRY KEY
,所以用id_
开始任何其他索引是不必要的,也是低效的。没有
WHERE
过滤,所以优化器可能会决定从ORDER BY
的索引开始,这是存在的。这也意味着它将在100行后停止。EXPLAIN
不太可能显示这种停止。FOREIGN KEY
是两个东西:INDEX
构造--同样好)如果ENGINE=InnoDB,则
PRIMARY KEY
将被静默地附加在每个辅助索引的末尾。因此,在上述索引 * 的末尾添加id_
* 是多余的(可选)。提示。这实际上可能有助于提高性能:
当你有
INDEX(a,b)
(或UNIQUE(a,b)
)时,不要也有INDEX(a)
。它是冗余的,浪费空间,并且可能欺骗优化器使用较短的索引而不是复合索引。(我在每个表定义中都看到一个这样的情况。)如果
EXPLAIN
未能将mv
列为第一个表,则有多种方法可以强制首先使用它。