我注意到,当服务器处于高负载时,我在一个相当大的表上执行的某个查询变得非常慢:
SELECT large.*, users.username
FROM large
LEFT JOIN users ON large.user_id = users.id
ORDER BY large.abc
LIMIT 30
(large.user_id
和users.id
都已编入索引。)
因此,经过进一步检查,我意识到这是由于连接查询,因为删除连接查询使其变为即时。此外,重写查询同样快。
SELECT t.*, users.username
FROM (
SELECT * FROM large ORDER BY large.abc LIMIT 30
) as t
LEFT JOIN users ON t.user_id = users.id
这是什么原因?是第一个在排序和限制之前加入所有行的查询,并且是解决此问题以使用第二个查询的最佳/唯一方法吗?
答案 0 :(得分:0)
我可以提出建议并尝试一下,并根据我的SQL小提琴让我知道吗
CREATE INDEX large_abc_index ON large(abc);
第一个查询似乎更简单,并使用正确的键。 我用explain命令来解释这里的查询。 http://sqlfiddle.com/#!9/784faf/1
答案 1 :(得分:0)
在您的第一个查询中,您将large
中的每一行加入其相应的用户,对large
中所有现已加入的行进行排序,然后丢弃除30个之外的所有行。在第二个查询中,您需要排序large
,丢弃除30之外的所有内容,然后将large
子集加入users
。因此,关键的区别在于连接了多少行。
我希望看到这些查询计划,但我怀疑第二个查询更快,因为临时表可读,与第一个查询相比,磁盘分页操作(它只有30行)要少得多(它有一个"大"行数)。
一般来说,子选择较慢,因为它们绕过了优化器的连接算法选择,并且必须完全依赖于解析查询,但在这种情况下,因为我们知道子选择只有30行,所以它似乎&# 39;比内置的连接优化器更好。
我怀疑如果您从子选择中删除LIMIT 30
并将其放在外部查询上,您会发现性能低于您的第一个查询。但是,同样,EXPLAIN
的输出对于解决这个谜团还有很长的路要走。
答案 2 :(得分:-2)
尝试根据此语法http://www.w3schools.com/sql/sql_join_left.asp
重新排序查询加入应该在订购之前完成。
SELECT large.*, users.username FROM large LEFT JOIN users ON large.user_id = users.id ORDER BY large.abc LIMIT 30