使用ORDER BY和LIMIT进行查询非常慢

时间:2011-03-09 17:03:29

标签: mysql

我的查询非常慢,因为使用了ORDER BY。现在我理解为什么它很慢但我不知道如何让它更快。

该表得到了900.000条记录。 (原因很慢)

SELECT SQL_NO_CACHE id, name 
FROM users where is_provider = 0 
AND activated_at IS NOT NULL
AND is_ngo = 0 
AND deleted_at is NULL 
AND is_cancelled = 0   
ORDER BY name 
LIMIT 60000, 90;

我使用限制因为我使用分页。 SQL_NO_CACHE因为我不想使用缓存进行测试。

此查询需要60秒,这太长了。这是一个后台任务,所以如果我能在5秒内减少它就可以了

我在activated_at列和deleted_at上有索引,它们是时间字段。其他的是布尔值,所以不需要索引。

由于

8 个答案:

答案 0 :(得分:3)

我认为索引是答案,但你必须弄清楚你的查询的正确索引,所以你应该试试这个:

EXPLAIN SELECT SQL_NO_CACHE id, name 
FROM users where is_provider = 0 
AND activated_at IS NOT NULL
AND is_ngo = 0 
AND deleted_at is NULL 
AND is_cancelled = 0   
ORDER BY name 
LIMIT 60000, 90;

在这篇非常古老但有用的文章中详细说明:
http://www.databasejournal.com/features/mysql/article.php/1382791/Optimizing-MySQL-Queries-and-Indexes.htm

答案 1 :(得分:2)

您可能会发现此文章有用:http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/

它涉及ORDER BY和LIMIT的组合。

答案 2 :(得分:1)

我猜这是按名称排序使它变慢。

要测试,请删除ORDER子句并检查所需的时间。

当您想要按名称命名时,名称字段可能应编入索引。

答案 3 :(得分:1)

我建议在最具选择性的布尔字段上添加索引,即,如果数据库中只有5%的行有is_provider = 0,那么您可以显着减少扫描所需的行数对于其他属性。如果分布是50/50那么没什么意义,但我会查看分布,并指导您确定哪些索引可能有用。当然,您应该以实际表现为指导(包括您可能拥有的其他查询)。

答案 4 :(得分:0)

或者,删除Order By子句并在阅读完记录后执行订购。它将工作放在客户端上,但如果数据库速度很慢(如此处所示),则可能更可靠,更快。

干杯,

丹尼尔

答案 5 :(得分:0)

名称列是否已作为另一个索引的一部分编入索引?

索引排序应该相对较快(不需要filesort),除非name恰好是索引的一部分,而不是第一部分。 点击此处:http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html

请发布您的EXPLAIN结果,以便我们进一步提供帮助......

答案 6 :(得分:0)

你可以尝试

SELECT  * FROM (SELECT SQL_NO_CACHE id, name 
FROM users where is_provider = 0 
AND activated_at IS NOT NULL
AND is_ngo = 0 
AND deleted_at is NULL 
AND is_cancelled = 0   
ORDER BY name) t
LIMIT 60000, 90;

避免ORDER BY和LIMIT在一起。

答案 7 :(得分:0)

这里的问题是,Mysql首先尝试Order ByName表中所有可用行,然后尝试以Where为条件选择记录。因此,您可以尝试的是首先根据Select条件创建记录Where,然后对结果集运行Order By name

示例:

SELECT  * FROM (SELECT SQL_NO_CACHE id, name 
FROM users where is_provider = 0 
AND activated_at IS NOT NULL
AND is_ngo = 0 
AND deleted_at is NULL 
AND is_cancelled = 0) t ORDER BY name
LIMIT 60000, 90;