我想知道是否有人会对如何优化这个MySQL查询有一些意见。我认为我正在使用索引做正确的事情所以不要认为我能更快地得到这个查询(它目前运行时间超过3秒)但是真的很喜欢有经验来证明我错误的人。
以下是查询:
SELECT `rooms`.*,
((IFNULL(SUM(av.host_daily_price), 0) +
rooms.host_daily_price * (4 - COUNT(DISTINCT av.id))) / 4) / 1 as 'price',
rooms.*,
(ACOS(least(1, COS(0.7115121627883911) * COS(1.291278129536698) *
COS(RADIANS(rooms.lat)) * COS(RADIANS(rooms.lng)) +
COS(0.7115121627883911) * SIN(-1.291278129536698) *
COS(RADIANS(rooms.lat)) * SIN(RADIANS(rooms.lng)) +
SIN(0.7115121627883911) * SIN(RADIANS(rooms.lat)))) * 3963.19) AS distance
FROM `rooms`
LEFT JOIN availabilities AS av
ON (av.room_id = rooms.id AND
av.date BETWEEN '2010-12-29' AND '2011-01-01')
WHERE (rooms.deleted_at IS NULL) AND
(`rooms`.`hidden` = 0) AND
(rooms.id <> 7713) AND
(rooms.city_id = 1 AND
rooms.max_guests >= 4 AND
rooms.minimum_stay <= 3 AND
rooms.room_type IN ('room','apartment','house')) AND
(av.inquiry_id IS NULL)
GROUP BY rooms.id
HAVING SUM(IFNULL(status, 0)) = 0 AND
(COUNT(*) = 4 OR `rooms`.default_available = 1)
ORDER BY distance ASC
LIMIT 12;
Explain的输出:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE rooms ref PRIMARY,index_rooms_on_city_id,index_rooms_on_room_type,index_rooms_on_city_id_and_updated_at index_rooms_on_city_id 5 const 2412 Using where; Using temporary; Using filesort
1 SIMPLE av ref index_availabilities_on_room_id,index_availabilities_on_room_id_and_date,index_availabilities_on_room_id_and_date_and_status index_availabilities_on_room_id 5 roomorama.rooms.id 79 Using where
如果有任何其他信息有用,请告诉我!
答案 0 :(得分:1)
您应该认真考虑存储距离而不是计算它,特别是如果您按该值排序。
答案 1 :(得分:1)
怎么样:
1 - 仅从房间获取一次所有数据。正如@OMG Ponies在他的评论中指出的那样,房间中的所有列都在查询中被提取两次。
2 - 如果对查询中的常量执行的trig函数是真正常量的(例如COS(0.7115121627883911)),则用计算值替换它们,即
COS(0.7115121627883911) = .7573753305446695179374104150422980521625
COS(1.291278129536698) = .2758925773610728508649620468976736490713
COS(0.7115121627883911) = .7573753305446695179374104150422980521625
SIN(-1.291278129536698) = -.9611884756680473394167554039882007538993
3 - 此查询似乎正在进行球形触发,以获得地球表面上两点之间的正确距离。通过执行类似
的操作来计算近似距离可能更快,同样有用dist = SQRT( (lat2-lat1)^2 + ((long2-long1) * COS(RADIANS(lat1+lat2)/2)))^2 ) * 60
这(应该)以英里为单位给出点(lat1,long1)和(lat2,long2)之间的距离。根据您喜欢的任何距离测量调整尾随常数。
分享并享受。
答案 2 :(得分:0)
如果没有看到您的表格如何编入索引,则很难判断是否存在任何特定问题。我不知道MySQL解释输出,所以我不会伪造任何东西。
但是,您可以做的一件事是创建条件索引。例如,除了拥有像
这样的公共索引之外create index rooms_by_id on rooms(room_id);
您可以拥有已经内置条件的索引,从而减少必须遍历的索引记录数
create index rooms_by_id_usable on rooms(room_id)
WHERE (deleted_at IS NOT NULL) and (hidden <> 0)
如果您的rooms
条记录中有20%与deleted_at IS NULL and hidden <> 0)
匹配,那么此替代索引rooms_by_id_usable
将比rooms_by_id
小20%,并且(大约)20%没有多少时间穿越。
这完全取决于MySQL优化器以及它如何选择使用索引等等。我在PostgreSQL中知道这确实很有效。
答案 3 :(得分:0)
如果移除距离计算,性能是否会得到显着改善?如果是这样,可能值得在房间表上存储lat和lng的sin和cos(并使用查询中存储的值) - 这些函数是相对处理器密集的,因此为所有相对较大的数据集导出它们可能对绩效产生重大影响。
答案 4 :(得分:0)
切换到PostgreSQL并使用PostGIS。它是PostgreSQL的地理空间扩展,可以本地高效地完成这类工作。
如果你真的坚持使用MySQL,那么没有骰子,但PostgreqSQL是免费的,开源的,易于使用的,而且速度快,所以如果你有这种能力,它是一个完全可行的MySQL替代品(至少可以说)选择。