Ускорение сложного многосоединения MySQL-запроса (созданного через CakePHP, если это имеет значение) - PullRequest
0 голосов
/ 11 июля 2011

У меня есть запрос MySQL ниже, который я использую, чтобы получить события для моей страницы списков событий. Проблема в том, что для запуска с пределом 10 требуется ~ 35 секунд, и еще ~ 35 секунд, чтобы выполнить СЧЕТ для разбивки на страницы. 70 + секунд время загрузки страницы просто не сократит , как вы можете себе представить. И это только с 740 результатами! Я боюсь думать, как это будет работать, когда мы получим 2000+.

Мы пытались индексировать (насколько нам не хватает знаний об индексах), и это имело буквально нулевой эффект.

Объяснение табличных ассоциаций: Событие может быть проведено в ресторане или месте проведения мероприятия. Город этого события определяется city_id ресторана или места его проведения. Это также получает Загрузки (фотографии в этом случае).

Несколько запутанной частью является Расписание / Дата - Расписание (я) содержит информацию о начале / окончании / повторении события. Записи Date создаются на основе информации расписания и содержат индивидуальную запись для каждого дня проведения мероприятия (начало = дата-время, конец = дата-время)

Я использую CakePHP для создания этого запроса и перечислил свои ассоциации внизу:

SELECT
`Event`.*, `Venue`.`id`, `Venue`.`slug`, `Venue`.`name`, `Venue`.`GPS_Lon`,
`Venue`.`GPS_Lat`, `Venue`.`city_id`, `VenueCity`.`name`, `VenueCity`.`slug`,
`Restaurant`.`id`, `Restaurant`.`slug`, `Restaurant`.`name`, `Restaurant`.`GPS_Lat`,
`Restaurant`.`GPS_Lon`, `Restaurant`.`city_id`, `RestaurantCity`.`name`, 
`RestaurantCity`.`slug`, GROUP_CONCAT(Date.start, "|", Date.end

ORDER BY Date.start ASC SEPARATOR "||") AS EventDates
FROM `events` AS `Event`
LEFT JOIN restaurants AS `Restaurant` ON (`Restaurant`.`id` = `Event`.`restaurant_id`)
LEFT JOIN venues AS `Venue` ON (`Venue`.`id` = `Event`.`venue_id`)
LEFT JOIN cities AS `VenueCity` ON (`Venue`.`city_id` = `VenueCity`.`id`)
LEFT JOIN cities AS `RestaurantCity` ON (`Restaurant`.`city_id` = `RestaurantCity`.`id`)
INNER JOIN schedules AS `Schedule` ON (`Schedule`.`event_id` = `Event`.`id`)
INNER JOIN dates AS `Date` ON (`Date`.`schedule_id` = `Schedule`.`id`)
LEFT JOIN uploads AS `Upload` ON (`Upload`.`event_id` = `Event`.`id`)
WHERE `Event`.`approval_status_id` = 1 AND `Date`.`start` >= '2011-07-11 12:38:54'
GROUP BY `Event`.`id`
ORDER BY `Date`.`start` ASC LIMIT 10

Ассоциации CakePHP:

Event belongsTo Venue
Venue hasMany Event

Event belongsTo Restaurant
Restaurant hasmany Event

Event hasMany Upload
Upload belongsTo Event

City hasMany Restaurant
City hasMany Venue
Restaurant belongsTo City
Venue belongsTo City

Event hasMany Schedule
Schedule belongsTo Event
Schedule hasMany Date
Date belongsTo Schedule

ОБНОВЛЕНИЕ (по запросу @Zoredache):

Это то, что я получаю, добавляя EXPLAIN перед выбором:

id  select_type  table          type  possible_keys            key              key_len   ref                             rows  Extra
1   SIMPLE       Event          ref   PRIMARY,approval status  approval status  5         const                           946   Using where; Using temporary; Using filesort
1   SIMPLE       Restaurant     ref   PRIMARY,id               id               4         medut_ent.Event.restaurant_id   1 
1   SIMPLE       Venue          ref   PRIMARY,id               id               4         medut_ent.Event.venue_id        1 
1   SIMPLE       VenueCity      ref   PRIMARY,id               id               4         medut_ent.Venue.city_id         1 
1   SIMPLE       RestaurantCity ref   PRIMARY,id               id               4         medut_ent.Restaurant.city_id    1 
1   SIMPLE       Schedule       ref   PRIMARY,index            index            5         medut_ent.Event.id              1     Using where; Using index
1   SIMPLE       Date           ref   all cols,start...        all cols         5         medut_ent.Schedule.id           8     Using where; Using index
1   SIMPLE       Upload         ALL                                                                                       4240  

Ответы [ 4 ]

2 голосов
/ 29 августа 2011
SELECT STRAIGHT_JOIN
`Event`.*, `Venue`.`id`, `Venue`.`slug`, `Venue`.`name`, `Venue`.`GPS_Lon`,
`Venue`.`GPS_Lat`, `Venue`.`city_id`, `VenueCity`.`name`, `VenueCity`.`slug`,
`Restaurant`.`id`, `Restaurant`.`slug`, `Restaurant`.`name`, `Restaurant`.`GPS_Lat`,
`Restaurant`.`GPS_Lon`, `Restaurant`.`city_id`, `RestaurantCity`.`name`, 
`RestaurantCity`.`slug`, GROUP_CONCAT(Date.start, "|", Date.end ORDER BY Date.start ASC SEPARATOR "||") AS EventDates

FROM `events` AS `Event`
INNER JOIN schedules AS `Schedule` ON (`Schedule`.`event_id` = `Event`.`id`)
INNER JOIN dates AS `Date` ON (`Date`.`schedule_id` = `Schedule`.`id`)
LEFT JOIN restaurants AS `Restaurant` ON (`Restaurant`.`id` = `Event`.`restaurant_id`)
LEFT JOIN cities AS `RestaurantCity` ON (`Restaurant`.`city_id` = `RestaurantCity`.`id`)
LEFT JOIN venues AS `Venue` ON (`Venue`.`id` = `Event`.`venue_id`)
LEFT JOIN cities AS `VenueCity` ON (`Venue`.`city_id` = `VenueCity`.`id`)
LEFT JOIN uploads AS `Upload` ON (`Upload`.`event_id` = `Event`.`id`)
WHERE `Event`.`approval_status_id` = 1 
AND `Date`.`start` >= '2011-07-11 12:38:54'
GROUP BY `Event`.`id`
ORDER BY `Date`.`start` ASC 
LIMIT 10
0 голосов
/ 11 июля 2011

Попробуйте избавиться от этой group_concat. Тот факт, что вы используете как временную таблицу, так и сортировку файлов, является признаком.

Вы также должны поставить индексы на все ваши внешние ключи, restaurant_id, venue_id и т. Д. Они должны включать ваш локальный идентификатор секунды, поэтому RestaurantCity, city_id and id`.

0 голосов
/ 13 июля 2011

Оказывается, таблица загрузки не имела индекса.Простое добавление индекса к этому делает его невероятно быстрым (142 мс вместо 75000 + мс).

Я обнаружил проблему с помощью EXPLAIN SELECT ... (благодаря @Zoredache) - подробности EXPLAIN в "ОБНОВЛЕНИИ""ответа.

0 голосов
/ 11 июля 2011

Предполагая, что индексация правильная, попробуйте переместить некоторые из ваших объединений, чтобы сначала использовать те, которые используются в предложении WHERE, а также использовать STRAIGHT_JOIN , чтобы убедиться, что ваши заказы не слишком оптимизированы MySQL:

SELECT STRAIGHT_JOIN
`Event`.*, `Venue`.`id`, `Venue`.`slug`, `Venue`.`name`, `Venue`.`GPS_Lon`,
`Venue`.`GPS_Lat`, `Venue`.`city_id`, `VenueCity`.`name`, `VenueCity`.`slug`,
`Restaurant`.`id`, `Restaurant`.`slug`, `Restaurant`.`name`, `Restaurant`.`GPS_Lat`,
`Restaurant`.`GPS_Lon`, `Restaurant`.`city_id`, `RestaurantCity`.`name`, 
`RestaurantCity`.`slug`, GROUP_CONCAT(Date.start, "|", Date.end ORDER BY Date.start ASC SEPARATOR "||") AS EventDates

FROM `events` AS `Event`
INNER JOIN schedules AS `Schedule` ON (`Schedule`.`event_id` = `Event`.`id`)
INNER JOIN dates AS `Date` ON (`Date`.`schedule_id` = `Schedule`.`id`)
LEFT JOIN restaurants AS `Restaurant` ON (`Restaurant`.`id` = `Event`.`restaurant_id`)
LEFT JOIN cities AS `RestaurantCity` ON (`Restaurant`.`city_id` = `RestaurantCity`.`id`)
LEFT JOIN venues AS `Venue` ON (`Venue`.`id` = `Event`.`venue_id`)
LEFT JOIN cities AS `VenueCity` ON (`Venue`.`city_id` = `VenueCity`.`id`)
LEFT JOIN uploads AS `Upload` ON (`Upload`.`event_id` = `Event`.`id`)
WHERE `Event`.`approval_status_id` = 1 
AND `Date`.`start` >= '2011-07-11 12:38:54'
GROUP BY `Event`.`id`
ORDER BY `Date`.`start` ASC 
LIMIT 10

Вы также можете быстрее выполнить отдельный запрос для дат, в отличие от оператора GROUP_CONCAT, поскольку возможно, что это может быть создание TEMP TABLES (что будет очевидно в вашем операторе EXPLAIN).

...