Mysql ORBERBY子句使查询速度变慢,尽管结果集很小
我有以下表格(删除不相关的内容):Mysql ORBERBY子句使查询速度变慢,尽管结果集很小,mysql,sql,performance,query-optimization,Mysql,Sql,Performance,Query Optimization,我有以下表格(删除不相关的内容): Booking包含约456k行,Payment包含约331k行。以下查询采用0.06秒并返回97行: select * from Booking b join Payment p on b.paymentId = p.id where p.status = 3 如果我添加一个order by子句,则查询将花费4.4s的时间,几乎慢了100倍: select * from Booking b join Payment p on b.paymentId = p.
Booking
包含约456k行,Payment
包含约331k行。以下查询采用0.06秒并返回97行:
select * from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
如果我添加一个order by
子句,则查询将花费4.4s的时间,几乎慢了100倍:
select * from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
order by b.nrOfPassengers
解释第一个查询:
id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, SIMPLE, p, ALL, PRIMARY, NULL, NULL, NULL, 331299, Using where
1, SIMPLE, b, ref, paymentFK, paymentFK, 9, p.id, 1, Using where
第二点:
id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, SIMPLE, p, ALL, PRIMARY, NULL, NULL, NULL, 331299, Using where; Using temporary; Using filesort
1, SIMPLE, b, ref, paymentFK, paymentFK, 9, p.id, 1, Using where
我使用MySQL 5.1.34
查询中使用的
where
子句过滤掉Payment
中的绝大多数行。我得到的印象是,MySQL在使用(高度选择性)where
子句过滤结果集之前对结果集进行排序。我说的对吗?如果是,为什么会这样做?我已尝试分析这两个表,但没有更改查询计划。首先,确保表上有适当的索引。假设您这样做了,但仍然比预期的慢,您可以将结果放入子查询中,而无需对其排序,然后将ORDER BY子句添加回:
SELECT *
FROM (
select * from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
)
ORDER BY nrOfPassengers
我不确定这会有多大帮助,因为当我查看执行计划时,它会添加一行,但它可能会更快
祝你好运。我怀疑,问题是在你删除的不相关内容中有一个
TEXT
或BLOB
列,它让MySQL冒险存储临时表的中间结果
在任何情况下,我们从执行计划中看到:
对于Payment
表中的每一行,从磁盘中取出它,检查条件,如果Booking
中的每一匹配行都为true,则将结果放入临时表中。按nrOfPassengers
对整个表中的所有数据进行排序并输出。如果存在Text
或Blob
字段,则中间表存储并排序在磁盘上,因为MySQL无法预测表的大小
您可以(像往常一样)最大限度地减少磁盘操作。按照@ajreal的建议,在status
列中添加索引。如果它是如此有选择性,您将不需要任何其他索引,但是如果您将您的paymentFK
扩展到(paymentId,nrOfPassengers)
则会更好。现在按如下方式重写查询:
SELECT p.*, b.*
FROM (
select p.id as paymentId, b.id as bookingId
from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
order by b.nrOfPassengers
) as ids
JOIN Payment p ON ids.paymentId = p.id
JOIN Booking b ON ids.bookingId = b.id;
数据将按子查询顺序输出。是否定义了索引?我有点惊讶,
paymentid
不是booking
主键的一部分。这确实令人费解。是否可能是由于达到RAM限制而导致虚拟内存占用?请定义列状态的索引,然后再试一次。您认为您的查询有何帮助?您的思路实际上非常有趣。该表不包含任何TEXT
或BLOB
列。只有VARCHAR
,INT
,BIGINT
,DECIMAL
,BIT
和DATETIME
。但是,在status
上添加索引会使原始查询和您的查询立即生效。我仍然无法理解为什么排序依据对原始查询的性能有如此大的影响。@ViktorDahl,第一个查询在排序之前不需要临时表来存储数据,并且无法预测第二个查询中表的大小,因为没有统计数据显示有多少行p.status=3
。
SELECT p.*, b.*
FROM (
select p.id as paymentId, b.id as bookingId
from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
order by b.nrOfPassengers
) as ids
JOIN Payment p ON ids.paymentId = p.id
JOIN Booking b ON ids.bookingId = b.id;