我有一个场景,我试图从超过200万行中获取用户的最新消息,并按其父(或线程)ID对它们进行分组。但是,这种分组会导致查询时间大约为1秒,比没有group by时慢1000倍。
这是table
CREATE TABLE `msg` (
`msg_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`msg_to` int(10) unsigned NOT NULL,
`msg_from` int(10) unsigned NOT NULL,
`msg` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL,
`date` timestamp NOT NULL DEFAULT current_timestamp(),
`parent` int(10) unsigned NOT NULL,
PRIMARY KEY (`msg_id`),
KEY `msg_toIX` (`msg_to`) USING BTREE,
KEY `msg_fromIX` (`msg_from`) USING BTREE,
KEY `parentIX` (`parent`) USING BTREE )
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
以下是我的查询
SELECT a.msg_id, a.msg_from, a.msg FROM msg a
JOIN(SELECT MAX(msg_id) maxid FROM msg WHERE msg_to = 23 GROUP BY parent ORDER BY msg_id DESC LIMIT 10) b
ON a.msg_id IN (b.maxid)
ORDER BY a.msg_id DESC LIMIT 10
解释
这是它得到的最好的结果吗?还是我应该得到更好的性能,因为我可以在0.001中提取1万行,并且没有group by子句?我是不是走错路了?
感谢您一直以来的指导和支持
- O.琼斯在下面的头上敲钉子。复合指数是缺失的一环
4条答案
按热度按时间6bc51xsx1#
为
parent
和msg_id
创建一个复合索引,以便优化每个父节点的最大ID。rsaldnfx2#
试试这个复合指数。它covers,我相信,你内心的疑问。
内部查询应该可以由这个索引上的range scan完成。
fquxozlt3#
| msg_id| msg_to| msg_from| MSG|日期|母|线程消息ID|
| --|--|--|--|--|--|--|
| 15 | 23 | 22 |O| 2023-01-01 16:34:56| 3 | 1 |
| 14 | 23 | 22 |n| 2023-01-01 15:34:56| 2 | 1 |
fiddle
v1uwarro4#
在大家的一点投入下,我将全面回答这个问题。
我最初在JOIN子查询中包含了LIMIT,因为这有助于加快速度。O.Jones关于3个字段的复合索引的建议帮助将查询时间从1秒降低到0.04秒,但我注意到结果忽略了大量的行。我在子查询中重新包含了ORDERBY,它纠正了结果,但将查询时间提高到了0.2s。
根据O.Jones的回答,将索引的顺序从(msg_to,parent,msg_id)切换到(parent,msg_to,msg_id)导致新索引将用于GROUP BY,将DERIVED表中的扫描行从100万+减少到61,查询时间减少到0.000秒,还允许省略子查询中的ORDER BY和LIMIT。
所以中的正确查询是
带索引:
谢谢大家。说实话,这是一个非常棒的问题,值得一些投票。我相信将来会有人发现它很有用:)