Нужна помощь в оптимизации сложного SQL-запроса - PullRequest
0 голосов
/ 15 октября 2010

Мне было интересно, есть ли у кого-нибудь информация о том, как можно оптимизировать этот запрос 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;

Вывод Объясните:

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

Дайте мне знать, будет ли полезна любая другая информация!

Ответы [ 5 ]

1 голос
/ 15 октября 2010

Как насчет:

1 - Получить все данные из комнат только один раз. Как отметил @OMG Ponies в своем комментарии, все столбцы из комнат выбираются дважды в запросе.

2 - Если функция триггера, выполняемая для констант в запросе, действительно постоянна (например, 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) в милях. Отрегулируйте трейлинг-константу для любого расстояния, которое вы предпочитаете.

Делись и наслаждайся.

1 голос
/ 15 октября 2010

Вам следует серьезно подумать о сохранении расстояния, а не о его расчете, особенно если вы сортируете по этому значению.

0 голосов
/ 15 октября 2010

Переключитесь на PostgreSQL и используйте PostGIS . Это геопространственное расширение PostgreSQL, которое делает подобные вещи естественным и эффективным образом.

Если вы по-настоящему застряли с MySQL, то не стоит забывать, но PostgreqSQL является бесплатным, открытым исходным кодом, простым в использовании и быстрым, так что это вполне жизнеспособная альтернатива MySQL (если не сказать больше), если у вас есть возможность выбирать.

0 голосов
/ 15 октября 2010

Значительно ли улучшается производительность, если удален расчет расстояния? Если это так, возможно, стоило бы хранить sin и cos lat и lng в таблице комнат (и использовать сохраненные значения в запросе) - эти функции относительно интенсивно используют процессор, поэтому их вывод для всех относительно больших наборов данных значительно влияет на производительность.

0 голосов
/ 15 октября 2010

Не видя, как индексируются ваши таблицы, трудно сказать, есть ли какие-то конкретные проблемы.Я не знаю вывод объяснения 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) 

Если 20% ваших rooms записей соответствуют deleted_at IS NULL and hidden <> 0), то этот альтернативный индекс rooms_by_id_usable будет на 20% меньше, чем rooms_by_id, и для его прохождения потребуется (примерно) на 20% меньше времени.

Все это зависит от оптимизатора MySQL и от того, как он решит использовать индексы и так далее.Я знаю в PostgreSQL, что это действительно очень хорошо работает.

...