2010-11-20 3 views
1

Я в затруднении. У меня есть таблица размером около 100 тыс. Строк. При запросе этой таблицы результаты обычно бывают быстрыми, около 2 мс или около того. Но всякий раз, когда я использую производительность ORDER BY, падает как камень до примерно 120 мс. Я читал страницу MySQL ORDER BY Optimization, но я не могу сказать, что все понимаю. Особенно мне неясно, какие индексы мне не понятны.Оптимизируйте запрос ORDER BY

В конце концов, я хотел бы выполнить следующий запрос:

SELECT * 
    FROM `affiliate_new_contracts` 
WHERE phone_brand IN ('Apple','Blackberry','HTC','LG','Motorola','Nokia', 
         'Samsung','Sony Ericsson') 
    AND contract_length IN ('12','24') 
    AND (addon IS NULL OR addon IN('Telfort Sms 300','Surf & Mail')) 
    AND (plan_name = 'Telfort 100' 
     AND 
     credible_shop = 1 
     ) 
    ORDER BY average_price_per_month ASC, phone_price_guestimate DESC, 
      contract_length ASC; 

Но я был бы рад, если бы я понял основные принципы.
Удаление предложения ORDER BY в предыдущем запросе заставляет его работать через 20 мс вместо 120 мс. У меня есть индекс в поле average_price_per_month, но упрощение предложения ORDER BY до ORDER BY average_price_per_month не привело к увеличению производительности. Этого я не понимаю. Я также в темноте о так называемых индексах с несколькими столбцами, которые должны быть в состоянии помочь мне с окончательным запросом.

Любая помощь будет оценена по достоинству. Как я могу сделать этого плохого мальчика? Или это квест утопический?

CREATE TABLE синтаксис выглядит следующим образом:

$ show create table affiliate_new_contracts; 
CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`), 
    KEY `index_affiliate_new_contracts_on_average_price_per_month` (`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

BTW Эта таблица воссоздан еженедельно и не обновляется в это время.

+1

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

+0

Отличный комментарий. Часть запроса была/была создана (больше доказательств я плохо подходит для запросов). Я удалил лишнее имя таблицы. – harm

ответ

3

Существует ограничение на то, какую оптимизацию вы можете сделать в предложениях ORDER BY. Первичный, который иногда помогает, имеет индекс в правильном наборе столбцов в правильном порядке. Так, для примера, а (одиночный, композитный) индекс:

average_price_per_month ASC, phone_price_guestimate DESC, contract_length ASC 

может помочь, но оптимизатор может все-таки решил, что лучше использовать другой индекс, чтобы иметь дело с условиями фильтра в запросе и то он сортирует данные, выбранные таким образом сами. Обратите внимание, что, если индекс не предоставляет данные точно в правильном порядке сортировки и с использованием индекса ускоряет запрос в целом, оптимизатор не будет его использовать. Индекс только для одного из столбцов, подлежащих сортировке, является ограниченным преимуществом оптимизатора и обычно не использует такой индекс.

один вопрос для рассмотрения:

  • Как быстро запрос выполнить без предложения ORDER BY.

Это дает вам очень непосредственное измерение стоимости сортировки. Вы указываете 20 мс без заказа и 120 мс с заказом, поэтому ORDER BY умеренно дорогой. Следующий вопрос может быть: «Можете ли вы превзойти его в своем приложении?». Возможно, вы сможете это сделать, но пакет сортировки в СУБД обычно довольно хорошо оптимизирован, и вам, вероятно, придется много работать, чтобы победить его.

0

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

Одним из решений было бы использовать составной индекс, который включает в себя наиболее избирательный столбец (название плана) и столбец упорядочения (average_price_per_month).После выбора все равно придется делать сортировку, но результаты уже будут упорядочены в колонке первичного заказа, что сократит время.

CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`,`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Вы также можете использовать EXPLAIN, чтобы понять, как запрос выполняется (если моя интуиция не правильно) и корректировать показатели соответственно.