2012-02-27 2 views
14

Моя ситуация:Как оптимизировать медленный запрос со многими присоединяется

  • запрос ищет около 90 000 автомобилей
  • запрос занимает много каждый раз, когда
  • У меня уже есть индексы на всех полях соединяемых.

Как его оптимизировать?

Вот запрос:

SELECT vehicles.make_id, 
     vehicles.fuel_id, 
     vehicles.body_id, 
     vehicles.transmission_id, 
     vehicles.colour_id, 
     vehicles.mileage, 
     vehicles.vehicle_year, 
     vehicles.engine_size, 
     vehicles.trade_or_private, 
     vehicles.doors, 
     vehicles.model_id, 
     Round(3959 * Acos(Cos(Radians(51.465436)) * 
         Cos(Radians(vehicles.gps_lat)) * 
              Cos(
              Radians(vehicles.gps_lon) - Radians(
              -0.296482)) + 
           Sin(
             Radians(51.465436)) * Sin(
           Radians(vehicles.gps_lat)))) AS distance 
FROM vehicles 
     INNER JOIN vehicles_makes 
     ON vehicles.make_id = vehicles_makes.id 
     LEFT JOIN vehicles_models 
     ON vehicles.model_id = vehicles_models.id 
     LEFT JOIN vehicles_fuel 
     ON vehicles.fuel_id = vehicles_fuel.id 
     LEFT JOIN vehicles_transmissions 
     ON vehicles.transmission_id = vehicles_transmissions.id 
     LEFT JOIN vehicles_axles 
     ON vehicles.axle_id = vehicles_axles.id 
     LEFT JOIN vehicles_sub_years 
     ON vehicles.sub_year_id = vehicles_sub_years.id 
     INNER JOIN members 
     ON vehicles.member_id = members.id 
     LEFT JOIN vehicles_categories 
     ON vehicles.category_id = vehicles_categories.id 
WHERE vehicles.status = 1 
     AND vehicles.date_from < 1330349235 
     AND vehicles.date_to > 1330349235 
     AND vehicles.type_id = 1 
     AND (vehicles.price >= 0 
      AND vehicles.price <= 1000000) 

Вот таблица транспортного средства схема:

CREATE TABLE IF NOT EXISTS `vehicles` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `number_plate` varchar(100) NOT NULL, 
    `type_id` int(11) NOT NULL, 
    `make_id` int(11) NOT NULL, 
    `model_id` int(11) NOT NULL, 
    `model_sub_type` varchar(250) NOT NULL, 
    `engine_size` decimal(12,1) NOT NULL, 
    `vehicle_year` int(11) NOT NULL, 
    `sub_year_id` int(11) NOT NULL, 
    `mileage` int(11) NOT NULL, 
    `fuel_id` int(11) NOT NULL, 
    `transmission_id` int(11) NOT NULL, 
    `price` decimal(12,2) NOT NULL, 
    `trade_or_private` tinyint(4) NOT NULL, 
    `postcode` varchar(25) NOT NULL, 
    `gps_lat` varchar(50) NOT NULL, 
    `gps_lon` varchar(50) NOT NULL, 
    `img1` varchar(100) NOT NULL, 
    `img2` varchar(100) NOT NULL, 
    `img3` varchar(100) NOT NULL, 
    `img4` varchar(100) NOT NULL, 
    `img5` varchar(100) NOT NULL, 
    `img6` varchar(100) NOT NULL, 
    `img7` varchar(100) NOT NULL, 
    `img8` varchar(100) NOT NULL, 
    `img9` varchar(100) NOT NULL, 
    `img10` varchar(100) NOT NULL, 
    `is_featured` tinyint(4) NOT NULL, 
    `body_id` int(11) NOT NULL, 
    `colour_id` int(11) NOT NULL, 
    `doors` tinyint(4) NOT NULL, 
    `axle_id` int(11) NOT NULL, 
    `category_id` int(11) NOT NULL, 
    `contents` text NOT NULL, 
    `date_created` int(11) NOT NULL, 
    `date_edited` int(11) NOT NULL, 
    `date_from` int(11) NOT NULL, 
    `date_to` int(11) NOT NULL, 
    `member_id` int(11) NOT NULL, 
    `inactive_id` int(11) NOT NULL, 
    `status` tinyint(4) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `type_id` (`type_id`), 
    KEY `make_id` (`make_id`), 
    KEY `model_id` (`model_id`), 
    KEY `fuel_id` (`fuel_id`), 
    KEY `transmission_id` (`transmission_id`), 
    KEY `body_id` (`body_id`), 
    KEY `colour_id` (`colour_id`), 
    KEY `axle_id` (`axle_id`), 
    KEY `category_id` (`category_id`), 
    KEY `vehicle_year` (`vehicle_year`), 
    KEY `mileage` (`mileage`), 
    KEY `status` (`status`), 
    KEY `date_from` (`date_from`), 
    KEY `date_to` (`date_to`), 
    KEY `trade_or_private` (`trade_or_private`), 
    KEY `doors` (`doors`), 
    KEY `price` (`price`), 
    KEY `engine_size` (`engine_size`), 
    KEY `sub_year_id` (`sub_year_id`), 
    KEY `member_id` (`member_id`), 
    KEY `date_created` (`date_created`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=136237 ; 

объясняю:

1 SIMPLE vehicles ref  type_id,make_id,status,date_from,date_to,price,mem... type_id  4 const 85695 Using where 
1 SIMPLE members  index PRIMARY  PRIMARY  4 NULL 3 Using where; Using index; Using join buffer 
1 SIMPLE vehicles_makes eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.make_id 1 Using index 
1 SIMPLE vehicles_models  eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.model_id 1 Using index 
1 SIMPLE vehicles_fuel eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.fuel_id 1 Using index 
1 SIMPLE vehicles_transmissions eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.transmission_id 1 Using index 
1 SIMPLE vehicles_axles eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.axle_id 1 Using index 
1 SIMPLE vehicles_sub_years eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.sub_year_id 1 Using index 
1 SIMPLE vehicles_categories  eq_ref PRIMARY  PRIMARY  4 tvs.vehicles.category_id 1 Using index 
+0

Можете ли вы предоставить ЭКСПЛАН? –

+0

Обновлено оригинальное сообщение. – ChimeraTheory

ответ

11

Улучшение ИНЕК

Ваш EXPLAIN показывает, что MySQL только с использованием одного индекса (type_id) для выбора строк, которые соответствуют пункту WHERE, даже если у вас есть несколько критериев в предложении.

Чтобы иметь возможность использовать индекс для всех критериев в предложении WHERE и как можно быстрее уменьшить размер результирующего набора, добавьте индекс нескольких столбцов в следующие столбцы таблицы транспортных средств:

(status, date_from, date_to, type_id, price) 

Столбцы должны быть в порядке наивысшей мощности не менее.

Например, vehicles.date_from, вероятно, имеют более четкие значения, чем status, поэтому поставить date_from колонку перед тем status, как это:

(date_from, date_to, price, type_id, status) 

Это должно уменьшить строки, возвращаемые в первой части выполнения запроса , и должен быть продемонстрирован с более низким количеством строк в первой строке результата EXPLAIN.

Вы также заметите, что MySQL будет использовать индекс с несколькими столбцами для WHERE в результате EXPLAIN. Если, случайно, это не так, вам следует намекнуть или заставить индекс с несколькими столбцами.

Удаление ненужных JOIN и

Это не кажется, что вы используете какие-либо поля в любой из соединяемых таблиц, поэтому удалите стыки. Это приведет к удалению всей дополнительной работы запроса и приведет вас к одному простому плану выполнения (одна строка в результате EXPLAIN).

Каждая таблица JOINed вызывает дополнительный поиск в строке набора результатов. Итак, если предложение WHERE выбирает 5000 строк из транспортных средств, так как у вас есть 8 соединений на транспортных средствах, у вас будет 5 000 * 8 = 40 000 запросов. Это очень много, чтобы спросить у вашего сервера базы данных.

+0

Если поля, которые мы сравниваем, связаны с объединенными таблицами, будет ли полезно индексирование? –

1

сделать вас также Inde XES на них:

vehicles.status 
vehicles.date_from 
vehicles.date_to 
vehicles.type_id 
vehicles.price 
+0

Да, см. Выше. – ChimeraTheory

1

быть немного более конкретными, чем @Randy индексов, я полагаю, что его намерение состояло в том, чтобы иметь индекс СОЕДИНЕНИЕ, чтобы воспользоваться вашим запрашивая critieria ... Один индекс, который построен на МИНИМУМ ...

(status, type_id, date_from) 

, но может быть расширен, чтобы включить date_to и цену тоже, но не знает, сколько показатель на этом уровне зернистого может реально помочь

(status, type_id, date_from, date_to, price) 

EDIT за Комментарии

Вам не нужны все эти отдельные индексы ... Да, первичный ключ сам по себе. Тем не менее, для других, у вас должны быть составные индексы, основанные на ваших общих критериях запроса и удалении других ... двигатель может запутаться, что может быть лучше всего подходит для запроса. Если вы знаете, что всегда ищете определенный статус, тип и дату (при поиске автомобилей), сделайте это как один индекс. Если запрос ищет такую ​​информацию, но и цены в рамках этих критериев, он уже будет очень близок к нескольким индексированным записям, которые квалифицируются и пролетают по цене как дополнительные критерии.

Если вы предлагаете такие запросы, как Only Automatic vs Manual, независимо от года/периода, то да, это может быть собственный индекс. Однако, если вы TYPICALLY имеете некоторые другие «общие» критерии, примените это как вторичное, которое МОЖЕТ быть использовано в запросе. Пример: если вы ищете Ручные трансмиссии, которые являются 2-дверными и 4-дверными, укажите свой индекс (transmission_id, category_id).

Опять же, вы хотите, чтобы все, что поможет сузить поле критериев, основанное на некотором «минимальном» состоянии. Если вы ссылаетесь на дополнительный столбец на индекс, который может быть «обычно», это должно только помочь производительности.

+0

Я не знаком с составными индексами - см. Мой обновленный пост. Является ли текущая индексация неэффективной? – ChimeraTheory

+0

Просто добавьте еще один индекс, но вместо столбца SINGLE просто выполните, как указано выше, несколько столбцов, разделенных столбцами ... Таким образом, один индекс может иметь несколько компонентов, чтобы более точно соответствовать критериям запроса. – DRapp

+0

У меня есть 21 индекс, который мне нужно сгруппировать и сделать, тогда мне нужно сначала удалить их? – ChimeraTheory

4

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

Самый простой пример - рассчитать минимальную/максимальную долготу и широту, которые вас интересуют, и добавить ее в пункт WHERE. Таким образом, расстояние будет рассчитываться только для подмножества строк.

WHERE 
    vehicles.gps_lat > min_lat ANDd vehicles.gps_lat < max_lat AND 
    vehicles.gps_lon > min_lon AND vehicles.gps_lon < max_lon 

Для более сложных решений см:

3

ли вы SQL быстрее без этого?

Round(3959 * Acos(Cos(Radians(51.465436)) * 
    Cos(Radians(vehicles.gps_lat)) * 
    Cos(Radians(vehicles.gps_lon) - 
    Radians(-0.296482)) + 
    Sin(Radians(51.465436)) * 
    Sin(Radians(vehicles.gps_lat)))) AS distance 

выполняет математические уравнения является очень дорогим

Может быть, вы должны рассмотреть материализованное представление, что предварительно вычислить вас расстояние, и вы можете выбрать из этой точки зрения. В зависимости от того, насколько динамичны ваши данные, вам может не потребоваться слишком часто обновлять данные.

+0

Немного да, но мне нужно, чтобы там ... – ChimeraTheory

Смежные вопросы