2013-07-19 117 views
8

我想弄清楚为什么我的一个查询速度慢,我如何解决它,但我对我的结果有点困惑。为什么MySQL在查询中使用LIMIT时速度很慢?

我有一个orders表周围80列和775179行和我做以下请求:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200

这在4.5S

返回38行当除去ORDER BY我很好的改进:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL LIMIT 200

分38排在0.30s

但没有接触ORDER BY我得到一个更好的结果取出LIMIT时:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC

38排在0.10s(??)

为什么我的LIMIT如此饥饿?

的进一步深入

送我的答案之前注意到,我有一个指数creation_date(这是一个datetime)我删除它和第一个查询现在0.10s运行后,我尝试了一些东西。这是为什么 ?

编辑

好猜,我对那里的人列部分索引。

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200; 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra  | 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 
| 1 | SIMPLE  | orders | index | id_state_idx,id_mp_idx | creation_date | 5  | NULL | 1719 | Using where | 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 

1行集(0.00秒)

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC; 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra            | 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
| 1 | SIMPLE  | orders | range | id_state_idx,id_mp_idx | id_mp_idx | 3  | NULL | 87502 | Using index condition; Using where; Using filesort | 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
+5

因为在第一次查询后,结果被(部分)缓存为以下查询... – Mr47

+1

事实并非如此,重新运行同样的查询给了我一致的结果 –

+0

也许你的服务器很慢.... – pattyd

回答

6

指标不一定提高性能。为了更好地理解正在发生的事情,如果您为不同的查询包含explain,将会有所帮助。

我最好猜测的是你有一个id_state甚至id_state, id_mp索引,可以用来满足where条款。如果是这样,没有order by的第一个查询将使用此索引。它应该很快。即使没有索引,也需要依次扫描orders表中的页面,该表格仍然非常快。

然后,当您在creation_date上添加索引时,MySQL决定使用该索引代替order by。这需要读取索引中的每一行,然后获取相应的数据页面以检查条件并返回列(如果匹配)。这种阅读效率非常低,因为它不在“页面”顺序中,而是由索引指定。随机读取可能效率很低。

更糟的是,即使您有limit,您仍需要阅读整个表,因为需要整个结果集。尽管您已对38条记录进行了排序,但您已经创建了大量低效的查询。

顺便说一句,如果orders表不适合可用内存,这种情况会变得更糟糕。然后你有一个叫做“thrashing”的条件,每个新记录往往会产生一个新的I/O读取。所以,如果一个页面上有100条记录,那么该页面可能必须被读取100次。

通过在orders(id_state, id_mp, creation_date)上设置索引,可以使所有这些查询的运行速度更快。 where子句将使用前两列,而order by将使用最后一列。

+0

很好的答案,但是在775179个记录表上,我会说索引是需要的吗? –

+1

@KayNelson。 。 。是。这就是为什么我在最后一段提出更好的指数。我想要小心一点,因为如果没有解释,答案都是猜测。 –

+0

是的,这是非常真实的! –

0

同样的问题发生在我的项目, 我做了一些测试,发现了该限制是因为行查找缓慢

参见: MySQL ORDER BY/LIMIT performance: late row lookups

所以,解决的办法是:

(A)当使用LIMIT时,请选择不是所有列,但只选择PK列

(B)选择您需要的所有列,然后加入结果集(A)

SQL应该喜欢:

SELECT 
    * 
FROM 
    orders O1 <=== this is what you want 
JOIN 
    (
     SELECT 
      ID       <== fetch the PK column only, this should be fast 
     FROM 
      orders 
     WHERE 
      [your query condition]  <== filter record by condition 
     ORDER BY 
      [your order by condition] <== control the record order 
     LIMIT 2000, 50     <== filter record by paging condition 
    ) as O2 
ON 
    O1.ID = O2.ID 
ORDER BY 
    [your order by condition]   <== control the record order 
在我的DB

老SQL其中选择使用 “LIMIT 21560,20” 中的所有列,费用约4.484s。

新的sql成本只有0.063s。新的速度大约快71倍

相关问题