使用Order By时,MySQL查询变得非常慢

时间:2017-04-01 15:20:15

标签: mysql performance query-optimization database-performance

我有一个包含1500万行的messages表。

以下查询在不到1秒的时间内返回约5百万(但限制在15行)记录:

SELECT messages.* FROM messages 
INNER JOIN gateways ON
messages.gateway_id=gateways.id
INNER JOIN orders ON
gateways.order_id=orders.id
WHERE orders.user_id=6500 AND messages.deleted=0
AND messages.type='Out' LIMIT 15;

但是当我在其末尾添加Order By id DESC时,它会在约40秒内变得极其缓慢:

SELECT messages.* FROM messages 
INNER JOIN gateways ON
messages.gateway_id=gateways.id
INNER JOIN orders ON
gateways.order_id=orders.id
WHERE orders.user_id=6500 AND messages.deleted=0
AND messages.type='Out' ORDER BY messages.id DESC LIMIT 15;

任何帮助都会非常感激。

2 个答案:

答案 0 :(得分:1)

模式 NSString *myString = @"@username is me: by using this as sample text"; NSRange range = [myString rangeOfString:@":"]; NSString *newString= [myString substringFromIndex:range.location]; NSLog(@"New String is : %@", newString); 因导致性能问题而臭名昭着。离开SELECT lots_of_stuff ORDER BY something LIMIT small_integer会导致性能问题消失。为什么?因为带有ORDER BY something的模式会导致MySQL服务器对大量相当大的行进行排序(在您的情况下为500万行),所以只丢弃其中的少数行。它在您的服务器中使用了大量的RAM,CPU和IO,只是为了丢弃大部分工作。

您最好的选择是在此处使用延迟连接类型的模式,除了ORDER BY值之外,您只能对其进行排序。使用此子查询来执行此操作。

message.id

这将为您提供15个 SELECT messages.id FROM messages INNER JOIN gateways ON messages.gateway_id=gateways.id INNER JOIN orders ON gateways.order_id=orders.id WHERE orders.user_id=6500 AND messages.deleted=0 AND messages.type='Out' ORDER BY messages.id DESC LIMIT 15 值的精美集合。

下一步是优化此子查询。我建议你在message.id表上尝试复合覆盖索引,其中包含messages列。这应该有助于加速它。

您可能还需要其他表上的索引。您应该考虑使用MySQL中的(deleted, type, id, gateway_id)函数来分析您的表现。

最后,使用少量EXPLAIN值集合来获取所需的messages.id行,就像这样。 (这是延迟连接;你推迟获取整行,直到你知道你需要哪些行。这样你就不必messages整个混乱了。)

修改ORDER上添加复合索引,以避免对该表进行全表扫描。这不是很大,但这可能会有所帮助。

gateways (order_id, id)

答案 1 :(得分:1)

我认为

  • 每个订单属于一个用户
  • 每个网关属于一个订单

因此,这个:

INNER JOIN gateways ON messages.gateway_id=gateways.id
INNER JOIN orders   ON gateways.order_id=orders.id
WHERE orders.user_id=6500 AND messages.deleted=0

可以改为英文:

“获取属于此用户的订单的网关”。

现在,要获取与此用户相关的最新消息,问题是我们可能会有许多不同的gateway_id(根据您的EXPLAIN大约为143),因此我们无法使用索引跳过排序。

嗯,我们可以像O. Jones所说的那样,但是有一个问题。以下是查询的简化版本:

SELECT ... FROM messages
WHERE gateway_id IN (1,2) ORDER BY id DESC LIMIT 10

如果我们在(id,gateway_id)上有索引,那么MySQL很可能会决定按降序扫描它。如果它快速找到10条消息“gateway_id IN(1,2)”那么它会很快。但是,如果这些gateway_ids包含非常旧的消息,或者根本没有消息,则可能需要扫描整个索引。

如果PK关系如我所描述的那样,我将在消息表中实现user_id列,然后允许索引(user_id,message_id),这将使查询时间远低于毫秒。